リセットについての考察 の履歴(No.25)
更新- 履歴一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- 電気回路/HDL/リセットについての考察 へ行く。
リセット信号の扱い†
FPGA 開発を始めた当初(半年前くらい?)、 リセット信号の取り扱いについてあまり深く考えておらず、 「何となくリセットが掛かりそうな回路」を書いて満足していました。
しかし、レーシングなどについて勉強してからよく考えてみると、 リセットには慎重な扱いが必要であることが分かってきて、 ここらで一度考え直そうと思い立ちました。
ところが、調べれば調べるほどいろんなことが出てきて、
ちょっと泥沼状態です・・・
(2010/09/03) 実機での検証もうまく行っているようなので、 始めて読んだときに読みやすいように大幅に書き直しました。
参考にした内容†
- 小林芳直著「定本 ASICの論理回路設計」CQ出版社
- 内容的にはちょっと古い気もしますが、レーシングやメタステーブル、スタティックハザードなど、 一目見ただけでは HDLコードに現れてこない注意事項について勉強するにはこれよりちゃんと書かれた本を 見つけられていません。リセットに特化した話が書かれていたわけではありません。
- http://marsee101.web.fc2.com/reset_of_fpga.html
- 主に回路規模の観点から同期リセットと非同期リセットの比較が行われています
- http://www.fpgarelated.com/usenet/fpga/show/40963-1.php
- STARTUP_SPARTAN3 等のモジュールを使い GSR により FPGA を初期化する話題
- http://www.mofeel.net/210-comp-arch-fpga/9908.aspx
- 非同期リセットに関する話題
- http://www.xilinx.com/support/documentation/white_papers/wp272.pdf
- リセット回路構成上の注意をまとめた Xilinx の White Paper
- http://www.xilinx.com/support/documentation/white_papers/wp275.pdf
- FPGA のプリミティブの機能に合わせて HDL 記述に少し気をつけるだけで、
実質的には同じ動作をする回路を半分程度のサイズにできることがあるという話
(Xilinx の White Paper) - http://japan.xilinx.com/xcell/xl55/jp55xcell_04.pdf
- 同期リセット回路は非同期リセット回路に比べてツールによる最適化が掛かりやすいという話
本稿とは話題が異なるが、Block RAM を推論させる際の HDL の記述方法に関する注意点も書かれていて、非常に参考になる - http://toolbox.xilinx.com/docsan/xilinx6/books/docs/sim/sim.pdf
- Synthesis and Verification Design Guide (Xilinx) 253ページに GSR 動作をシミュレータで再現する方法が書かれている
- http://natu.txt-nifty.com/natsutan/2008/03/fpga_05d1.html
- 非同期リセット回路で、リセットのデアサートタイミングをクロック同期にする方法
- http://japan.xilinx.com/support/documentation/user_guides/j_ug332.pdf
- 図12-13 に Spartan3A のコンフィグレーション後の GSR の動作が分かるブロックダイアグラムがある
- http://www.fpgarelated.com/usenet/fpga/show/7379-3.php
- GSR の使い方(結論出ず)
- http://www.xilinx.com/itp/xilinx5/data/docs/sim/sim0057_10.html
- Understanding the Global Reset and Tristate for Simulation (Xilinx)
後から見つけた情報なので、まだ内容を取り込めていません。
- http://www.xilinx.com/itp/xilinx5/data/docs/sim/sim0058_10.html
- Simulating VHDL (Xilinx)
- Simulating Verilog
- Simulating Verilog (Xilinx)
同期リセットと非同期リセット†
良く知られるように、リセット信号を持つ回路の構成方法には、 非同期リセットと同期リセットがあって、 たとえば ISE の Language Template にも両方の形が収録されています。
同期リセットと非同期リセットのどちらが回路規模的にお得かは、 ASIC/FPGA/CPLD どれに実装するか、またどのメーカの FPGA に実装するか、 などによっても変わってくるそうです。
ASIC ではリセットを使う回路と使わない回路とで 実際にゲート1つ分の 回路規模と伝達遅延が差として現れるため、どのラッチにリセットを付けて、 どこに付けないかによって、回路規模およびパフォーマンスに直に効いてきます。
これに対して(Xilinx の)FPGA では全ての(通常の) FF にリセット入力ピンが 始めから備わっているので、ここに直接リセット信号を繋ぐ限り、 演算器の回路規模や回路遅延への悪影響はありません。 (もちろんリセットネットの配線リソースは消費します)
Xilinx FPGA の FF は同期リセット用(FDRSE)あるいは 非同期リセット用(FDCPE)にコンフィグレーションすることができ、 SET/RESET (FDRSE: 同期) あるいは PRE/CLR (FDCPE: 非同期) 入力が リセットピンとなります。
ただ、リセットピンを使えば全て解決かというと、
これにはいくつか制限があって、以下のような注意事項が挙げられています。
(wp272より抜粋)
- 配線リソース・配線遅延
- 多くの場合、リセットネットにはかなりの量の配線リソースが消費される
- リセットネットはしばしば非常に大きなファンアウトを持つので、 その遅延時間がクロック周期の最小値を制限してしまうこともある
- リセットネットに要求される最大遅延量を満たすために、他の信号ネットの遅延が増加する場合がある
- リセットネットを張るためにルーティングにかかる時間が増加する
- ロジックリソース
- FF に内蔵されたリセット・クリア機能が使えれば余計なロジックは消費されない
- 1つのスライスに含まれる2つの FF へのリセット線は共有されるため、 2つの FF が異なるリセット信号を持つ場合には1つのスライスに入れられなくなる
- リセット・セットあるいはクリア・プリセットと、クロックイネーブルとの 信号線の複数が同時にアサートされたときの優先順位はプリミティブに固有なので、 それに沿わない優先順位で HDL を記述すると余計なロジックが挿入されて、 速度や回路規模に悪影響を与えることになる
- リセット用のロジックのために PAR にかかる時間が増加する
- FF 自体を削減する、という最適化が働かなくなる
- FF がリセットを持たない場合には多数の FF を SRL16 を使って実装することで FF の消費を抑えることができる場合があるが (理想的には16個の FF を1つの LUT に押し込めることができる)、 リセットを持つ FF にはこの最適化が働かない (SRL16E にはリセット入力がないため)
- Distributed RAM や Block RAM にもリセット入力はないので、 FF を RAM として実装するような最適化も働かない
- LUT / RAM をリセットする機構は存在しない
- DFF をリセットする機能はプリミティブに含まれていますが、 LUT に実装された SRL16E や Distributed/Block RAM をリセットする機構は プリミティブに存在しないため、もし必要であれば初期化用のステートマシンを 組む必要がある
リセット・セット・クロックイネーブルの優先順位に関する補足:†
例えば FDRSE は Flipflop : D-type : Reset : Set : Enable の略で、 Reset, Set, Enable はこの順で優先順位が高いため、 次のコードは1つの FDRSE で無理なく実装できます。
LANG:verilog always @(posedge clk) if (reset) begin c <= 0; end else if (force_high) begin c <= 1; end else if (enable) begin c <= a & b; end
これをちょっとだけ変えて、次のように enable と force_high の優先順位を入れ替えると FDRSE のそれと異なってしまうため、この部分に余計なロジックが挿入されてしまいます。
FDRSE はプリミティブとして存在するが、FDRES は存在しないところがキモです。
LANG:verilog always @(posedge clk) if (reset) begin c <= 0; end else if (enable) begin if (force_high) begin c <= 1; end else begin c <= a & b; end end
もし回路の仕様としてどちらの動作でも構わないのであれば、 前者を採用することで回路規模を低減できます。
enable と reset を入れ替えたり、force_high と reset を入れ替えても同じです。
同期リセットと非同期リセット†
Xilinx の FPGA では非同期リセットに比べて同期リセットの方が 回路規模に関して有利であるという結果が FPGA の部屋 で得られているそうです。
http://marsee101.web.fc2.com/reset_of_fpga.html
この理由は Xilinx の 「デザイン パフォーマンス向上のためのHDLコーディング法」で詳しく解説されていて、 同期リセット回路は非同期リセット回路に比べてツールによる 最適化が掛かりやすいという話が紹介されています。
同期リセットで行くべき†
したがって、手で記述するリセットは非同期ではなく同期型にしておくのが良いようです。
リセット信号のデアサートは慎重に†
同期リセットを使う場合にも、非同期リセットを使う場合にも、 リセット信号のデアサートのタイミングには十分な配慮が必要になります。
リセットの長さ†
同期リセットを使う場合、リセット信号がアサートされる時間は(複数ある場合には最長の) クロック周期よりも長い必要があって、そうでないと最低限1回、 クロックエッジの時点でリセットがアサートされている状況を保証できません。
ただ、この問題はリセット信号を複数クロックに渡りアサートすることで、 簡単にクリアできるため、現実問題としてはあまり注意を払う必要は無いと思います。
デアサートのタイミングによっては正しく初期化できない†
リセット線をデアサートするタイミングが悪いと、 回路が誤動作する例を考えてみました。 (Xilinx の White Paper (wp272) にもほぼ同じ例がありました)
次のコードは同期リセットを持つフリーランニングのワンホットコーディング・ ステートマシンのつもりで書いたものです。
LANG:verilog reg [3:0] state; always @(posedge clk) if (rst) state <= 4'b0001; else state <= { state[2:0], state[3] }; // ローテート
この rst に、例えばプッシュボタンからの非同期信号をそのまま繋いでしまうと、 タイミングによっては正しくリセットが行われないと思います。
これは、rst が非同期信号であるために、いわゆるレーシングを生じるためです。
以下にもう少し詳しく考えてみます。
上記 if 文は3項演算子で書くことができて、その場合には次のようになります。
LANG:verilog reg [3:0] state; always @(posedge clk) state <= rst ? 4'b0001 : { state[2:0], state[3] }; // ローテート
さらにプリミティブの動作を分かりやすくするためにビット毎に分けると 次のようになります。
LANG:verilog reg [3:0] state; wire [3:0] initial_state = 4'b0001; wire [3:0] rotated_state = { state[2:0], state[3] }; // ローテート always @(posedge clk) begin state[0] <= rst ? initial_state[0] : rotated_state[0]; state[1] <= rst ? initial_state[1] : rotated_state[1]; state[2] <= rst ? initial_state[2] : rotated_state[2]; state[3] <= rst ? initial_state[3] : rotated_state[3]; end
ここまで書くと、この回路で rst が同期化されていない場合の問題が明らかになります。
- rst = 1 が数クロック続いて state == 4'b0001 になる
- rst が 0 に変化する時刻は、ネット遅延により、各ビットで少しだけ異なる
- rst==0 が state[3:1] にくらべてほんのわずかに速く state[0] に伝わると、
- state[0] <= rotated_state[0]; // == 1'b0
- state[1] <= initial_state[1]; // == 1'b0
- state[2] <= initial_state[2]; // == 1'b0
- state[3] <= initial_state[3]; // == 1'b0
となる可能性があって、この場合、リセット直後にホットビットが失われてしまい、 回路はまともに動作しません。
同様に、バイナリカウンタを定数値からデクリメントして使うような場合も、 おかしな結果を生む可能性のある例になると思います。
非同期リセット回路でも†
非同期リセット回路でもリセットをデアサートするタイミングと クロックエッジとが重なれば同じ状況が起きると思います。
クロックのデアサートをクロックエッジに同期する†
上記問題の最も簡単な回避方法は、 同期リセット回路へ入力するリセット信号デアサートを クロックエッジに同期することだと思います。
LANG:verilog input wire async_rst, input wire clk, ... // リセット信号の同期化 wire sync_rst; double_ff( .idata(async_rst), .oclk(clk), .odata(sync_rst) ); // 同期化されたリセットを、同期リセット回路に入力 reg [3:0] state; always @(posedge clk) if (sync_rst) state <= 4'b0001; else state <= { state[2:0], state[3] }; // ローテート
ここで使っている double_ff は 非同期信号を扱うための危ういVerilogライブラリ で紹介しているモジュールで、 非同期信号を同期化するための二重 FF 回路です。
非同期リセットの場合†
非同期リセット回路においても、リセットデアサートタイミングをクロックに同期することができて、 その方法については、下記サイトが参考になりました。
http://natu.txt-nifty.com/natsutan/2008/03/fpga_05d1.html
が、少なくとも Xilinx の FPGA では同期リセットの方が 回路の最適化上で格段に有利とのことなので、以下では同期リセットのみ考えます。
複数のクロック信号がある場合†
上と同じ考え方でいくと、もし回路が複数のクロック信号を持っているならば、 リセット信号もクロックの種類と同じだけの種類用意して、 必ずクロックに同期したリセット信号で回路を初期化しなければならない ことになります。
クロックに同期したリセット信号を使っていれば、 同期リセット回路に間違って異なるクロックに同期した リセット信号を入力してしまったりしても、 ツールが遅延時間解析で見つけてくれるので、 うっかりミスも防げます。
同期リセットの問題点†
このように、すべてのリセットを同期リセットとして、 クロックに同期したリセットのデアサートを意識的に行うことで、 "理論上は" 安全なリセット回路ができあがります。
ただ、実際にこの方針で回路設計を行うと、規模が小さい間は良いのですが、 大きくなるに従って無理が出てきます。
1つには多くのクロックを使う場合、複数のリセットネットの伝達のために 多くの回路要素を消費してしまうことです。
もう1つは大きなリセットネットはネット内の遅延時間が大きくなるため、
- リセット信号のネット遅延がクロック周波数を制限してしまったり、
- リセット信号線の最適化のためにルーティング時間が非常に長く掛かったり、
- リセット信号線の最適化のために他の信号線の遅延時間が長くなったり、
といった悪影響が現れてきます。
対症療法1:BUFG を利用する†
配線リソースを節約し、さらに 巨大なファンアウトを持つリセットネットの伝達遅延を最小限に抑えるため、 リセット信号の伝達に BUFG を使うことを検討する余地があります。
FPGA の部屋の marsee さんによれば Xilinx のセミナーでも 紹介された手法らしいです。
http://marsee101.blog19.fc2.com/blog-entry-1464.html
リセット信号を BUFG を通して配線することで ネットの遅延も小さくなりますし、ローカルな配線リソースを消費しない分、 他の配線に与える影響も小さくなるはずなので、BUFG が余っていれば試す価値がありそうです。
この場合、やはり回路は同期リセット回路にしておくのが良いのだと思います。
クロックと同じ数だけ BUFG が余っているなら、 この方法で問題はほぼ解決できそうです。
対症療法2:同期リセット信号ネットの分割†
リセットのデアサートのタイミングは、 密接に関連する複数の FF の間で揃っていれば良く、 関連の無い FF の間では異なっても構わないので、 同期リセット信号のネットを複数に分割することで、 ネット内の遅延が大きくなりすぎるという問題を 解決できる場合があります。
すなわち、モジュールの入り口まで (できれば BUFG を使って) 非同期リセット信号を届けておき、必要なモジュールでのみ、 もージュール内部で同期リセット信号を作って使う方法です。
リセット信号の同期化のために各所で FF を2つずつ消費する ことになりますので、配線リソースを省けるメリットと、 FF の消費とを考え合わせて、どちらが良いかを見積もることになります。
リセットのデアサートタイミングが問題にならない場合もある†
極端な安全志向から、すべてを同期リセット回路で構成し、 それらにクロックに同期したリセット信号を届ける方針を採る場合、 上で見たように、使用リソース量およびタイミング的に非常に大きな負担となります。
ただ、すべての FF で非同期なリセットのデアサートが問題になるかというと、 そうでもありません。
例えばバイナリカウンタをゼロからインクリメントする場合や、 初期ステートが無条件で遷移せず、外部信号のアサートを待つような回路、 あるいは始めの2ステートがジョンソンカウンタの無条件遷移になっている場合など、 リセット信号の非同期なデアサートにより問題が生じない回路も多数存在します。
そこで、レーシングが問題になる箇所のみを同期リセットで記述することにして、 そうでないところには同期化していないリセット回路をそのまま入れてしまう、 あるいは以下に示す GSR を用いる方法により、リセット回路そのものを省いてしまう ことが可能です。
特に、GSR を使う方法では、 リセットを実装するためのリソースをほとんど無視できる程度まで 小さくできそうです。
GSR の利用†
GSR とは Global Set Reset 信号の略で、これをアサートすることにより
全ての FF をコンフィグレーション直後の値(初期値)にリセットすることができます。
(Block/Distributed RAM および LUT に実装されたシフトレジスタ、DCM を除く)
GSR の動作はちょうど、隠された非同期リセット信号線があると考えれば良くて、 これは Xilinx の FPGA にもともと備わっている機能ですので、 この機能を使うために追加で配線リソースやロジックリソースが消費されることはありません。
ここで言う FF の初期値とは、Verilog で reg を宣言する際に指定する値です。
LANG:verilog localparam st_idle = 3'b000; reg [2:0] state = st_idle; // この st_idle のこと
あるいは INIT 属性などで指定することもできます。
LANG:verilog (* INIT="1" *) reg enable;
コンフィグレーション直後の値と、リセット直後の値とを異なる値にするのは 余程の事情があるときだけだと思うので、それ以外では非同期リセットの代わりに GSR を使うことで大幅にリソースを削減できます。
(メモ)GSR により Block RAM の出力レジスタの値も初期化されるそうです。
(Block RAM の中身については初期化されません)
STARTUP モジュール†
GSR を使うには STARTUP_???? というような名前のプリミティブを使うことになります。
???? の部分には FPGA の名前が入るので、Spartan3 であれば STARTUP_SPARTAN3 とか STARTUP_SPARTAN3A などとなります。
このモジュールには GSR という入力があるので、これを1にすることで全ての FF に非同期リセットがかかります。
Altera の FPGA では reg の初期値は使えない?†
ふnさんからのコメントで、Altera の FPGA では reg に与えた初期値は一見うまく動作するように見える物の、 低確率で正しく初期化されない場合があることを教えていただきました。
つまり、Altera の FPGA では初期値の必要な reg は必ずユーザーロジックで初期化する必要があることになります。
http://www.altera.co.jp/literature/hb/qts/qts_qii51007_j.pdf など Altera の出している文書にも (少なくとも VHDL では) reg の初期値を指定できるように書かれているため、 「指定してもうまく行かない場合がある」 というのはまずいようにも思うのですが・・・
reg に初期値を与えるというのは あまり積極的に使われていない機能なのかもしれませんね。
今考えている最善の方針†
ということで、現状で最善策と考えているのは、
- すべてのビットについてクロック同期のリセットが必要かどうかを慎重に見極める
- クロック同期のリセットが必要な回路には、正しくクロックに同期したリセット信号を届ける
- そうでない部分は GSR で再初期化することにより対応する
という方針です。
ただ、GSR を手動リセットに使うのは少々検討の必要な部分もあって、 以下にそのような点を考察していこうと思います。
シミュレーションで GSR をエミュレートする方法†
GSR 信号を有効活用する上でまず壁になるのが、 GSR の動作をシミュレーションで確認する方法です。
Xilinx FPGA ではコンフィグレーション直後の 100ns 程度の間、 自動的に GSR がアサートされ、デバイスはリセット状態を保ちます。
普通に ISE から ModelSim や ISim を起動してデバッグすれば、 ユーザーコードの他に Xilinx の glbl モジュールがインプリメントされ、 シミュレーションの起動から 100 ns 程度の間 GSR がアサートされることで、 この動作がエミュレートされます。
GSR をユーザー回路からコントロールするには、上記の通り STARTUP_SPARTAN3 モジュールを使うことになります。
ビヘイビャーレベルシミュレーション†
ただ、これらの方法で GSR がアサートされても、 ビヘイビャーレベルのシミュレーションでは GSR の動作が反映されません。 すなわち、GSR がアサートされている間にも 通常通りレジスタの書き換えが行えてしまいます。
考えてみればそれはそうで、恐らく Verilog 言語には GSR のような 非明示的なリセット回路を記述する構文はないのだと思います。
ではどうするかというと、 http://toolbox.xilinx.com/docsan/xilinx6/books/docs/sim/sim.pdf の253ページに方法が例示されています。
個々のレジスタに対して特別に GSR に対応するための記述を行っておき、 テストベンチから個々のモジュールに GSR 信号を配信するというものです。
コード例†
次の例は以下の動作を行う回路を書いたものです。
- 非同期リセット信号を使って GSR をドライブし FPGA 内の全ての FF を初期値に戻す
- 同時に同期リセット信号を生成し、 非同期リセット信号がデアサートされてから 256 クロック経ってからデアサートする
このコードを使って GSR 動作のシミュレーション方法を解説します。
この回路で GSR の影響を受けるのは rst および count です。
実機では STARTUP_SPARTAN3A の .GSR 入力に1を立てることでこれらの FF は初期値に固定されます。
しかし、何も特別なことをしなければシミュレーション上ではこの動作が 再現されません。
そこで、GSR による FF の動作をエミュレートするため
- GSR_in というダミーの非同期リセット信号線を用意する
- 論理合成時には
- この信号線はどこからもドライブされないためゼロのままになる
- if (GSR_in) というような条件式は決して成立しないため、最適化により削除される
- シミュレーション時には
- 上位のモジュールから GSR_in に glbl.GSR を繋ぐことで非同期リセットをアサートする
- if (GSR_in) 部分のコードで FF の初期化をエミュレートする
というように記述します。
LANG:verilog module reset_gen #( parameter COUNTER_BITS = 8 ) ( input wire GSR_in, // ダミーの非同期リセット信号線 input wire async_rst, input wire clk, output reg rst = 1 ); // GSR により すべての FF に async_rst による非同期リセットがかかる STARTUP_SPARTAN3 STARTUP_SPARTAN3A_inst ( .CLK(), // Clock input for start-up sequence .GSR(async_rst), // Global Set/Reset input (GSR can not be used as a port name) .GTS() // Global 3-state input (GTS can not be used as a port name) ); // 同期リセット信号の生成 reg [COUNTER_BITS-1:0] count = 0; always @(posedge GSR_in or posedge clk) begin if (GSR_in) begin // GSR 動作をエミュレート // 合成時には最適化により削除されるので初期値は別途指定しなければならない rst <= 1; count <= 0; end else begin // 0 からのインクリメントに同期リセットは必要ない count <= count + 1; if ( count == {COUNTER_BITS{1'b1}} ) rst <= 0; end end endmodule
この GSR_in 信号は、トップモジュールから下位モジュールへ供給されますが、 トップモジュールにドライブ源が記述されないため、論理合成時にはすべての if (GSR_in) は成立せず、GSR_in に関する記述は完全に無視されることになります。
LANG:verilog module project_top #( parameter COUNTER_BITS = 8 ) ( input wire async_rst, input wire clk, ... ); wire GSR_in; // ドライブ源がないため論理合成時には無視されるダミーの信号線 wire sync_rst; reset_gen reset_gen ( .GSR_in(GSR_in), .async_rst(async_rst), .clk(clk), .rst(sync_rst) ); ... endmodule
シミュレーション用にコンパイルするときのみ上位モジュールの GSR 信号線で下位モジュールの GSR_in をドライブします。
これは、テストベンチからトップモジュールの project_top.GSR を glbl.GSR でドライブすることにより行えます。
LANG:verilog module project_top_test; ... project_top uut ( ... ); assign project_top.GSR = glbl.GSR; ... endmodule
ということで、これらの記述はちょっと面倒ではあります。
- すべてのレジスタについて if (GSR_in) によるダミーの初期化文を記述しなければならないこと
- 複雑な階層の末端のモジュールまで、テストベンチから GSR 信号を届けなければならないこと
の2つは、通常の非同期リセットでも同じなため我慢できるとして、
- reg signal = initial_value; という部分と、if (GSR_in) signal <= initial_value;
の2カ所に初期値を記述しなければならず、DRY (Google:Don't repeat yourself)
の原則に反する
もちろん、1つ定数を用意してそれを2カ所で使えば原理的には問題ないのだけれど
と言う点については、うまい回避策が思い浮かばず、 嫌な感じが残ります。
互換性について†
上記のように if (GSR_in) でダミーの初期化ルーチンを入れておくことは、 ビヘイビャーレベルのシミュレーションをするためだけでなく、 コードの互換性を保つためにも非常に役立ちます。
というのも、上記の方針で書いたコードを GSR が利用できない環境で 使うことを考えた場合、if (GSR_in) の記述がない限り、大幅な手直しが 必要となってしまうためです。
if (GSR_in) の記述があれば、実際にここに非同期リセット線を繋いでしまえば、 GSR が利用できない環境でも同じコードをそのまま使えることになります。
ですので、上記方針に従っている限り、GSR を使うことでコードの互換性が 失われるという心配は無いと思います。(もちろんトップモジュールの リセット信号生成部分だけは書き換えなければなりませんが)
WARNING:Xst:1426 について†
上記のリセット生成部に次の警告が出ました
WARNING:Xst:1426 - The value init of the FF/Latch rst hinder the constant cleaning in the block reset_gen. You should achieve better results by setting this init to 0.
日本語のヘルプを読んでも意味が分からないのですが、
http://japan.xilinx.com/support/answers/18424.htm
英語版の説明を読んだらやっと理解できました。
http://www.xilinx.com/support/answers/18424.htm
この警告は、
- ある reg に初期値(GSR直後の値)が指定されている
- その reg の入力ピンには定数が繋がれている
- 初期値と定数の値が食い違っている
という状況で発生するそうです。(まさに上の状況ですね)
「初期値と定数が同じ値であれば、reg を取り除いて定数に置き換えが可能なので、 合成ツールはそうしたいのだけれど、初期値があるせいでできない」と文句を言っている みたいです。
通常は reg をそんな形に使うことはないため、 記述ミスの可能性があると言うことで、警告が出ています。
今の場合はまさにそういう使い方をしたくて記述しているので、 この警告は無視して良さそうです。
Post-PAR シミュレーションでは†
上で考察したとおり、 GSR を用いた回路についてビヘイビャーレベルのシミュレーションを行うには、 GSR の動作を模した非同期リセット線を独自に実装し、 そのリセット線を glbl.GSR でドライブするという、 非常に大回りな回避策を採らなければならなりませんでした。
Post-PAR シミュレーションなどでは、この問題は多少易しく解決できるようです。
というのも、simprims に含まれる X_SFF などのフリップフロップ モジュールは 内部で glbl.GSR を読み取ることで、 外部回路が無くても GSR 状態を正しく再現するようにできているためです。
つまり、glbl.GSR さえ正しくドライブしてやれば、 GSR がらみの動作を正しく Post-PAR シミュレーションできることになります。
ただし1つ注意が必要なこととして、STARTUP_SPARTAN3 などのモジュールは Post-PAR シミュレーションモデルでは何ら効果を発揮しないという点があります。
すなわち、STARTUP_SPARTAN3 の .GSR 入力を駆動しても、 PAR シミュレーションにおいて glbl.GSR が変化することはありません。
glbl.GSR などの動作は合成結果とは別レベルで動いているため、 STARTUP_SPARTAN3 の合成結果から glbl.GSR を駆動するわけにはいかない、 ということのようですね。
正しくシミュレーションするには STARTUP_SPARTAN3 の .GSR ピンへ入力した信号で glbl.GSR を駆動してやることになります。
上記のように、トップレベルモジュールの信号線で STARTUP_SPARTAN3 を駆動していれば、テストベンチから次のようにして glbl.GSR に接続できます。
LANG:verilog assign glbl.GSR = uut.GSR_out;
同期リセットの形で書く†
上記回路では律儀に GSR_in を非同期リセットとするために
LANG:verilog always @(posedge GSR_in or posedge clk) begin ...
のように書いていたのですが、
LANG:verilog always @(posedge clk) begin ...
のようにして、同期リセットの形にした方が便利そうです。
というのも、非同期の形にしていると Synthesis で
ERROR:Xst:899 : The logic for <xxx> does not match a known FF or Latch template. The description style you are using to describe a register or latch is not supported in the current software release.
というエラーが生じることがあるためです。
これは、XXX に GSR_in で初期値を与えない時に出るエラーです。
GSR_in はどうせシミュレーションでしか使いませんし、 上記回路での同期と非同期との差は GSR_in が複数クロックアサートされる限り ほとんどありません。
always の @ には GSR_in を記述しない方針で行こうと思います。
本件、GSR が使えない環境に持って行った場合には 実際に影響が出ることになりますが・・・
そういう場合に同期リセットがよいのか、 非同期リセットがよいのかは事前に予測できませんので、 書きやすい方を選んでおいても罰は当たりませんよね???
GSR_in のイディオム†
というわけで、 GSR_in を非同期リセットとして使うときの定型は次の形が直感的かと思います。
LANG:verilog reg reg_with_init1 = INIT_VALUE1; reg reg_with_init2 = INIT_VALUE2; reg reg_with_init3 = INIT_VALUE3; reg reg_without_init1; reg reg_without_init2; reg reg_without_init3; always @(posedge clk) begin ... <通常の処理 (reg_with_init? の他、reg_without_init? などへの代入も含む> ... if (GSR_in) begin // 初期値を持つ reg のみを初期値で上書きする register_with_init1 <= INIT_VALUE1; register_with_init2 <= INIT_VALUE2; register_with_init3 <= INIT_VALUE3; end end
これを、
LANG:verilog reg reg_with_init1 = INIT_VALUE1; reg reg_with_init2 = INIT_VALUE2; reg reg_with_init3 = INIT_VALUE3; reg reg_without_init1; reg reg_without_init2; reg reg_without_init3; always @(posedge clk) begin if (GSR_in) begin register_with_init1 <= INIT_VALUE1; register_with_init2 <= INIT_VALUE2; register_with_init3 <= INIT_VALUE3; // GSR_in が立っている間、通常処理はまったく行われない end else begin ... <通常の処理 (reg_with_init? の他、reg_without_init? などへの代入も含む> ... end end
のように書くのも有りなのですが、 この場合、reg_without_init? に関する動作も GSR_in でストップします。
実際には、reg_without_init? の内いくつかは通常の FF としてインプリメントされるため、GSR によりデフォルト値である 0 に固定されます。
そして、残りのいくつかはシフトレジスタやメモリとしてインプリメントされるため、 GSR 中にも値が変わることになります。
実際の所、本当に正しいのは FF としてインプリメントされる reg_without_init? のみを前者の形で if(GSR_in) begin ... end 中で ゼロ に固定することなのですが・・・
初期値を持たない reg の値が GSR_in 中に正しい値に初期化されるかどうかを見るという目的には、 たぶん前者の方が「それらしい」動作になると思います。
GSR 信号の延長†
コメント欄で marsee さんから、GSR 信号線をリセットとして使うと、 リセット状態が解除された時点でクロックが安定している保証がないので 気をつけた方が良いと指摘していただいたので、これについて考えてみました。
まず、Xilinx の ug332 の12章あたり、特に 図12-13 を見ると、STARTUP モジュールから出力される GSR 信号は、STARTUP_WAIT=TRUE のときすべての DCM の LOCKED 信号がアサートされるのを待ってから、 さらに 100 ns 程度経った後にデアサートされるみたいです。
したがって、もし LOCKED 信号が信頼できるようなら、 クロックの安定度については心配ないのかもしれません?
実際のところ「電源オン直後にクロックが不安定な可能性」というのは ちょっと誤解を生む表現かもしれないです。
FPGA の場合には、ボードに電源が供給され、 電圧が十分に安定した後、FPGA のコンフィグレーションが行われます。
そしてコンフィグレーションが終了するまでの間、かなりの時間にわたりクロック源はクロック供給を続けますので、コンフィグレーションが終わり、GSR がリリースされた段階で未だ外部クロックが安定していないとすれば、恐らく追加で数ミリ秒待ったとしても安定供給は期待できないのではないかと思います。
したがって、コンフィグレーション直後にクロックの安定が気になるとすれば、コンフィグレーションでパラメータが決まった後、DCM が安定にクロックを出力するまでに掛かる時間に関するものになるのではないでしょうか。
もしそうならば、DCM の LOCKED 信号を基準として、100 ns 待ち、それよりもさらにユーザーロジックで追加の待ち時間を入れることで、初期化の確実性が向上するのかどうか、というあたりが論点になりそうに思えます。
(2010.6.29追記)marsee さんからさらに書き込みをいただきました。 コンフィグレーションをパラレルで高速に行った場合には、 外部クロックの起動時間よりもコンフィグレーションに掛かる時間の方が短くなる可能性があって、 その場合には、実際に追加で数ミリ秒の待ち時間が必要になることもあるのだそうです。
回路設計の前に、まじめに発振子のスペックと、 コンフィグレーション時間とを比べてみる必要がありそうです。
ユーザーロジックを使ってさらに延長することを試みる†
こういうことが問題になる背景には、GSR がアサートされている限り通常の FF を使えないため、 例えば「DCM の LOCKED がアサートされてから 200 クロック後にリセットを解除する」 というようなロジックを組むのに工夫がいるという点があります。
以下では通常の FF の代わりに LUT をシフトレジスタとして使うことでカウンタを構成し、 DCM の LOCKED 後に GSR をリリースするタイミングを測ることを考えました。
GSR 中にも動作するカウンタ回路†
次の回路により GSR 中かどうかにかかわらず N クロック ( N <= 16 ) に一度、 トリガを出すことができます。
GSR 中には通常の FF を使ったカウンタを作れないことに注意して下さい。
ここでは GSR に影響を受けない LUT により構成されたシフトレジスタを用いているところがキモです。
16bit のシフトレジスタを使ってワンホットカウンタを構成しています。
LANG:verilog(linenumber) // N クロックに1度トリガを出力する module srl16_pulser #( parameter N = 10 ) ( input wire clk, input wire en, output wire pulse ); localparam BIT = N - 1; SRL16E #( .INIT(16'h0001) // Initial Value of Shift Register ) srl16e ( .Q(pulse), // SRL data output .A0(BIT[0]), // Select[0] input .A1(BIT[1]), // Select[1] input .A2(BIT[2]), // Select[2] input .A3(BIT[3]), // Select[3] input .CLK(clk), // Clock input .CE(en), // Clock enable input .D(pulse) // SRL data input ); endmodule
3つ繋げれば、1000 クロックに一度 1 を出力するような回路も簡単に作れます。
LANG:verilog(linenumber) // それぞれ 10 clk / 100 clk / 1000 clk に一度 1 パルスを出力する // パルス幅は 1 clk / 10 clk / 100 clk wire pulse10, pulse100, pulse1000; srl16_pulser #(10) pulser10 ( .clk(clk), .en(1), .pulse(pulse10) ); srl16_pulser #(10) pulser100 ( .clk(clk), .en(pulse10), .pulse(pulse100) ); srl16_pulser #(10) pulser1000 ( .clk(clk), .en(pulse10 & pulse100), .pulse(pulse1000) ); // 1 clk 幅のパルスにしたければ and を取る // 1000 clk に 1 clk だけ 1 になる wire pulse_out = pulse10 & pulse100 & pulse10000;
この回路はカウンタあたりで LUT を1個だけしか使わないので、 非常に省スペースのカウンタとして、GSR の話とは関係ない所でも、 結構使い手があるかもしれません?
問題はカウンタのリセット回路がないことで、 大抵の用途ではそれが致命的なんですが(汗
より安全なカウンタ回路†
上記のカウンタは parameter の指定により任意の周期を簡単に作れるのが良いものの、 パワーオン直後などクロックが不安定な状況で、 万が一ホットビットが失われてしまうと、 まったくトリガを発しないまま、永久に復帰できません。
さらに、リセット回路もないため、そのような状況に陥った場合、 リコンフィグレーションする以外に復旧できないことになります。
より安全な回路として、周期は 32 固定になってしまいますが SRLC16 を使ったジョンソンカウンタを作ってみました。
これならば万が一起動がうまく行かなくても、最悪周期が短くなるだけで済みます。
また init 入力を 16 クロック間アサートすることで、 カウンタを初期化できるようになっていますので、 本当に最悪の場合には手動リセットを行えます。
というわけで、まずまず安全に使えるんじゃないかと思います。
LANG:verilog(linenumber) // GSR 中にも動作する16ビットジョンソンカウンタ (32クロック周期でトリガを発生) module srl16_pulser32 ( input wire clk, input wire init, // 16クロック間アサートすることで初期化 input wire en, output wire pulse ); wire Q14, Q15; localparam A = 14; SRLC16E #( .INIT(16'h0001) // Initial Value of Shift Register ) GSR_counter_l ( .Q15(Q15), // Carry output .Q (Q14), // SRL data output .A0(A[0]), // Select[0] input .A1(A[1]), // Select[1] input .A2(A[2]), // Select[2] input .A3(A[3]), // Select[3] input .CLK(clk), // Clock input .CE(en | init), // Clock enable input .D(!Q15 & !init) // SRL data input ); assign pulse = !Q14 & Q15; endmodule
GSR を延長する†
上記 srl16_pulser32 を使い、async_rst が下りてから (EXTENTION + 1) x 2^20 クロック後に GSR を解除するコード (実際には GSR_out を上位モジュールで STARTUP_SPARTAN3 に入れる)を書いてみました。
ここで EXTENTION には 15 以下の整数を指定します。
LANG:verilog(linenumber) module gsr_gen #( parameter EXTENTION = 10 // (EXTENTION + 1) x 2^20 クロックだけ延長する ) ( input wire async_rst, input wire clk, input wire GSR_in, // 論理合成時に無視されるダミーの非同期リセット output wire GSR_out // GSR 信号出力 ); // GSR 状態を検出する reg GSR_active = 1; always @(posedge GSR_in or posedge clk) if (GSR_in) begin GSR_active <= 1; end else begin GSR_active <= 0; end // 32 クロックに一度パルスを出す (GSR 中にも動作する) wire pulse32; srl16_pulser32 pulser32 ( .clk(clk), .en(!async_rst), .init(!GSR_active), .pulse(pulse32) ); // 1024 クロックに一度パルスを出す (GSR 中にも動作する) wire pulse1024; srl16_pulser32 pulser1024 ( .clk(clk), .en(!async_rst & pulse32), .init(!GSR_active), .pulse(pulse1024) ); // 32768 クロックに一度パルスを出す (GSR 中にも動作する) wire pulse32768; srl16_pulser32 pulser32768 ( .clk(clk), .en(!async_rst & pulse32 & pulse1024), .init(!GSR_active), .pulse(pulse32768) ); // 1048576 クロックに一度パルスを出す (GSR 中にも動作する) wire pulse1048576; srl16_pulser32 pulser1048576 ( .clk(clk), .en(!async_rst & pulse32 & pulse1024 & pulse32768), .init(!GSR_active), .pulse(pulse1048576) ); wire pulse = pulse32 & pulse1024 & pulse32768 & pulse1048576; // GSR を (EXTENTION + 1) x 2^20 クロック延長する wire gsr_extended; SRL16E #( .INIT(16'hffff) // Initial Value of Shift Register ) GSR_counter_h ( .Q(gsr_extended), // SRL data output .A0(EXTENTION[0]), // Select[0] input .A1(EXTENTION[1]), // Select[1] input .A2(EXTENTION[2]), // Select[2] input .A3(EXTENTION[3]), // Select[3] input .CLK(clk), // Clock input .CE(!async_rst & (pulse | !GSR_active) ), // Clock enable input .D(!GSR_active) // SRL data input ); // GSR 出力 assign GSR_out = async_rst | ( GSR_active && gsr_extended ); endmodule
例えば基本クロックが 125 MHz の時、4つ重ねると約 8.4ms の周期が得られますので、 EXTENTION パラメータを 4 とすることで 42ms の遅延が得られます。
普通なら 125 MHz を 2^24 分の1にするには 24 ビットのカウンタを使います。 それには 24 個の FF が必要になります。上記回路では SRL16E でカウンタを 作っているおかげで、5個のLUTだけで同じ処理ができています。 (実際には周辺ロジックのためにいくつか追加で LUT が必要になりますが、 FF を使った場合にはバイナリカウンタを作るためより多くの LUT が必要に なりますので、優位性は失われないと思います)
srl16_pulser32 は、GSR 延長用回路としても面白いですが、 省スペースカウンタとしてもいろいろ使いどころがあるかもしれません。
同期リセット信号を生成†
GSR とは独立に動作するので切り離しました。
LANG:verilog(linenumber) module reset_gen #( parameter COUNTER_BITS = 8 ) ( input wire clk, input wire GSR_in, // 論理合成時に無視されるダミーの非同期リセット output reg rst = 1 ); // 同期リセット信号の生成 => GSR 後 2**COUNTER_BITS だけ経ってから rst を下げる reg [COUNTER_BITS-1:0] count = 0; always @(posedge GSR_in or posedge clk) begin if (GSR_in) begin // GSR 動作をエミュレート(合成時には最適化により削除される) rst <= 1; count <= 0; end else begin // 0 からのインクリメントに同期リセットは必要ない count <= count + 1; if ( count == {COUNTER_BITS{1'b1}} ) rst <= 0; end end endmodule
使い方†
使うときは、async_rst にリセット端子からの入力と dcm_locked 信号の and を入れることで、 パワーオン直後、クロックが安定してからさらに数百クロック後に GSR を解除し、 さらにその後数百クロック後に同期リセットを解除する回路になっています。
GSR のネット遅延が FPGA 全体でどのくらいになるのか記述を見つけられなかったのですが、 さすがに数百クロック待っても伝わらないなどということは無いと思うので、 本当に非同期で構わない部分のみ GSR で初期化し、残りを同期リセットで初期化するという 方針で危険はないんじゃないかと思っています。
LANG:verilog module top_module( ... input wire clk_in, input wire async_rst_n, // アクティブ・ロー ... ); ... wire GSR; // ドライブ源がないため論理合成時には無視されるダミーの信号線 wire GSR_out; ... // GSR を生成 gsr_gen gsr_gen ( .async_rst(!async_rst_n | !dcm_locked), .GSR_in(GSR), .GSR_out(GSR_out), .clk(clk_in) // DCM からのクロックではなく外部クロックで駆動する ); // GSR により すべての FF に async_rst による非同期リセットがかかる // シミュレーション時にはテストベンチで以下のように接続する必要がある // assign glbl.GSR = GSR_out; // assign GSR_in = glbl.GSR; // ビヘイビャーレベルシミュレーション時 // GSR を持たないデバイスでは次のようにすれば通常の // 非同期リセットとして合成することができる // assign GSR_in = GSR_out; STARTUP_SPARTAN3A STARTUP_SPARTAN3A_inst ( .CLK(), // Clock input for start-up sequence .GSR(GSR_out), // Global Set/Reset input (GSR can not be used as a port name) .GTS() // Global 3-state input (GTS can not be used as a port name) ); // 同期リセット信号を生成 wire sync_rst_pre; reset_gen reset_gen ( .GSR_in(GSR), .clk(clk), // DCM からのクロックで駆動してもOK .rst(sync_rst_pre) ); // 同期リセット信号をグローバルな高速ネットを使って配信 wire sync_rst; BUFG sync_rst_buf ( .I(sync_rst_pre), .O(sync_rst) ); ...
実際の回路の構成としては、
上記の要領で if (GSR_in) の形で、 あたかも非同期リセットが掛かっているかのような記述をしながら、 実際には reg の初期値で非同期リセットを実現しつつ、
どうしても同期リセットが必要な部分には sync_rst をそのまま、 あるいはこれをローカルに別のクロックに同期したリセット信号を作成し、 同期リセット回路に入力することで実装しようと考えています。
同期リセット信号の配信にはグローバルな高速配線リソースを使っているので、 ローカルな配線リソースを圧迫することはなく、また、 ネット遅延によりクロック周期を圧迫することも少なくできると思います。
GSR による reg の初期化について考察†
すべての reg に初期値を付けるのは得策ではない†
なぜなら、(正しく論理合成がされる限り)初期値付きの reg に対しては、
reg をシフトレジスタや distributed ram, block ram として論理合成するといった、
高度な最適化が働かなくなってしまうためです。
GSR による初期化であっても、必要な reg のみに初期値を付けることで、
最適な結果を期待できます。
(2010/08/09追記)上記記述について、考え直したところ自信が無くなりました。
Xilinx の FPGA では分散 RAM やシフトレジスタに初期値を与えることができますが、 その初期値はコンフィグレーション直後の値を指定するためだけの物で、 GSR による再初期化では、それらの初期値は無視されることになります。
GSR で再初期化したい FF には、通常の FF としてインプリメントされるよう 明示的に制約を掛けておかないと、最適化によって正しくリセットが掛からない 可能性があるのかもしれません???
本件、早急に調査が必要に思えてきました。
コメント†
リセットの取り扱いについて†
[ふn] (2010-07-03 (土) 12:22:22)
ちょっと、時期を逃してしまいましたが・・・『reg の宣言時に初期値』についてですが、Altera 社のデバイスでは保証しない、と言われました。確かにQuartusのハンドブックにも、できるようなことを書いてありましたが、運が悪いとどちらに転ぶかわからないそうです。
- 重要な書き込みありがとうございます。これは、Altera社のセミナーか何かで得られた情報でしょうか?多くの場合できるけれど、保証はしない、というのはバグを仕様と言い張るように聞こえますが、ベンダーの発表であれば仕方がないですね。注意書きとして本文中に入れさせていただこうと思います。 -- [武内(管理人)]
- 日本アルテラに問い合わせた結果、「保証できません」との回答を得ました。リセット信号の解除時にクロックとちょうどよいタイミングではまると、どちらに転ぶかわからないそうです。そのような訳で、リセット信号は同期化したものを使用するように、とのことでした。 -- [ふn]
- 実験もしてみましたが、やっぱりアルテラのいう通りの結果となりました。実験はMaxIIを使いましたが、アルテラ社のデバイスは全てに対して適用されるとのことでした。これは2009年の話ですから、今年に入ってから改善・・・それはないかな? -- [ふn]
- お返事ありがとうございます。素人目にはリセット信号の解除がクロックに同期しているかどうかで初期値が変化するというのは不思議に思えました。一点確認させていただきたいのですが、これは上記で書いているレーシングなどとは異なる次元の問題なのですよね? -- [武内(管理人)]
- ユーザーリセットやGSRの解除をどのタイミングで行うべきか、という話題と、コンフィグレーションやGSR時にreg宣言時の初期値が反映されるか否か、という話題とを区別して議論したいと思います。reg 宣言時の初期値が(1の時には?)デバイスのコンフィグレーションに反映されないのであれば、かなり重大な問題ですね。そういうデバイスをターゲットとする限り、同期か非同期かに関わらず、何らかのユーザーリセットが必要になるのは間違いないと思います。 -- [武内(管理人)]
- 対して、これまでネット上の文献で調べた限り Xilinx のデバイスでは通常の FF に割り当てられたすべての reg は GSR のアサートによって初期値に固定されることが保証されているように読めました。ただ、GSRネットの伝達遅延が規定されていないという点に十分に注意を払って利用しないと、GSR のデアサート時にレーシングの問題が生じるため、(その意味をしっかりと理解できないのであれば)GSR をユーザーリセットに使うことはあまり推奨しないというような記述も目にしました。まだしっかりと実機でテストできていないのですが、書かれている内容は納得のいくものなので、恐らくその通りの動作をするのだろうと期待しています。(どこかに穴がないと良いのですが・・・) -- [武内(管理人)]
- 『デアサートのタイミングによっては正しく初期化できない』とは別に考えた方がよいと思います。アルテラでは、『reg の宣言時に初期値』を保証しないというののは、コンフィギュレーションが完了してユーザモードに移る際に、クロックとぶつかるとどう動くかわからない、ということだったと思います。ですので、reg a_reg = 1; と記述したとしても、ユーザモードに入ったときに、この a_reg は "1" であることが保証されない、ということです。もちろん reg a_reg = 0; と記述したとしても、コンフィギュレーション完了後には "0" であるという保証はされない、とのことです。 -- [ふn]
- 『reg 宣言時の初期値が(1の時には?)デバイスのコンフィグレーションに反映されない』という問題です。反映されないは言い過ぎですが、反映されない場合がある、ということです。かなりの高確率で反映はされます。 -- [ふn]
- 詳細な情報をありがとうございます。とてもよく分かりました。何だか、反映するように作ったはずがうまく動かなかったので仕様の方を替えた、という雰囲気が強いですが本当にうまく動かない場合があるのであれば、ユーザー側は「反映されない」ことを前提に設計するしかありませんね。 -- [武内(管理人)]
- ありえない回路ですが、1000ビット程度のシフトレジスタでLSBとMSBのビットを接続した回路を作り、reg [999:0] a_reg = '1; (1000ビットに"1"をセット)と宣言し、リセットなしのalways文で記述しました。温度や電圧に関係なく、30000回に1〜3回くらいは、1000ビット中のどこかのFFが反転するものでした。宣言時にAll"1"だけではなく、All"0"でも試しましたが、結果は同様です。ですので、初期値をセットした場合に反転してしまう確率はかなり低いものですが、初期値を保証できるというレベルではありません。 -- [ふn]
- 数万のFFを使う回路規模なら千回のリセットで1回しくじる感じですか。GSRのデアサート時にクロックイネーブル線が落ちていれば問題ない、というような前提条件がもしあれば、かなり確率は下がるかもしれませんが・・・低いとはいえ、ある一定の確率でしくじることが分かっていると、無視して使う気にはなりませんね。 -- [武内(管理人)]
リセットの取り扱いについて†
[marsee] (2010-06-22 (火) 13:34:32)
Xilinxのセミナでは、リセットが必要ないところは使わないようにした方が良いとのことでした。
XilinxのFPGAは、コンフィギュレーションする時にD-FFをすべて0にしています。よって、初期値が0で良いならばリセットする必要がないわけです。シミュレーションの時に困ることがあると思いますが、それでいいならば、リセットしない方が回路規模や色々なタイミングを満足するために良いとの事でした。
- はい、リセットはできる限り省くのが良いみたいですね。Verilog では本文中に(後から)記述した形式で reg の宣言時に初期値を決めれば 0 以外の初期値も可能なように思うのですが・・・正しいでしょうか? -- [武内(管理人)]
- 論理合成ツールがXSTの場合は可能だそうです。たぶん、Synplifyなどでは無視されるのだと思います。 -- [marsee]
- ちょっと調べてみたところ、おっしゃるとおり Synplify ではだめなようでした。http://tinyurl.com/2c22jfw によれば /* synthesis xc_props="INIT=S" */ というような属性で1にするか0にするかは選べるみたいですが、ベクター全体が1か0になってしまうため、適当な定数を初期値とするのは難しいようですね。 -- [武内(管理人)]
- と思ったら、http://tinyurl.com/25gwa2h では Synplify でも初期値を使えると書いてありますね。上の記述は 2005 年で、こちらは 2009 年なのでこの間に対応したと言うことかもしれません。が、この記事でも初期値は使えるツールと使えないツールがあるため、ポータビリティが低いという雰囲気になっています。同じ記事で Quartus でも使えると書いてありますので、最新版のツールを使う限りターゲットが FPGA であれば大抵大丈夫なのかもしれません。後から ASIC に焼き直すとかいう可能性がなければ気にしなくても??? -- [武内(管理人)]
- XSTもSynplifyも使える環境にあるので、後で本当はどうなのか確かめてみます。私は少なくともうちののシステムはリセットをしようと思っています。理由はクロックが電源ONから安定とは限らないからです。 -- [marsee]
- GSRを使う文脈をいろいろ考えてはみているのですが、クロックが安定するまで待つ方法を始め、おっしゃる通りいろいろ難しいところがありそうですね。今はそのあたりをシミュレーションで確かめる方法を検討しているのですが、STARTUP_SPARTAN3A の GSR ピンを上げてもレジスタ値は初期化されていないようで・・・何か根本的に違うことをしているみたいです? -- [武内(管理人)]
- ビヘイビャーレベルのシミュレーションで GSR の動作を正しくエミュレートするには http://toolbox.xilinx.com/docsan/xilinx6/books/docs/sim/sim.pdf の253ページにあるように、個々のレジスタに対して特別な記述をしなければならないのですね。これはかなり面倒そうです。また GSR はネット遅延が規定されていないので手動リセット回路としては使わない方が良いという記述がちらほらと。これは非同期であることを正しく考慮できるなら気にしなくても良いのかもしれませんが・・・ いずれにせよ、また考え直さないと行けないみたいです。 -- [武内(管理人)]
- 「電源オン直後にクロックが不安定な可能性」というのは、水晶発振器自体が電源ONの時に不安定な状態を抜け出す待ち時間が必要と言う意味で使いました。例えば、私たちが使ったEG-2121CAは、発振開始時間が10ms MAXです。これでマスタのコンフィギュレーションならば大丈夫かもしれませんが、スレーブセレクトマップだと問題が出る可能性があります。なかなかコメントでは説明が難しいですね。http://ndap3-net.ebz.epson.co.jp/w/www/PDFS/epdoc_qd.nsf/a2256996cd15b6cd49257074002d15eb/1fb0e0141cc1205a4925707c00291790/$FILE/EG-21xxCA_J09X.pdf -- [marsee]
- FPGAをスレーブにしてパラレル通信で高速に書き込んでしまうと、クロックが安定するよりもコンフィグレーションの方が早く終わってしまうこともあるのですね。私の頭にはマスタ SPIでのコンフィグレーションしかなかったので、思い至りませんでした。一応、上記のようにすればGSRでもリセットのデアサートに遅延を入れられるので、いざとなれば数十ミリ秒を待つこともできそうに思えるのですが、まだ実機で試していないのでうまく行ったらまた報告しようと思います。 -- [武内(管理人)]
- あれ、マスタ SPI でも FPGA のサイズが小さかったりすると結構危ういですね。10 ms というのが周波数がどの程度の範囲に入るまでの時間なのか、データシートからは分からなかったのですが、この時間をしっかり待とうとするとユーザーロジックでの待機が必要となることが理解できました。 -- [武内(管理人)]
- 遅くなりましたが、XSTとSynplify で初期化の推論がどうなるか?やってみました。両方同じように推論できるようです。http://marsee101.blog19.fc2.com/blog-entry-1521.html -- [marsee]
- 検証ありがとうございます。ふnさんの投稿によれば、reg に初期値を与えるやり方は Altera では安心して使うことのできない機能のようですが、Xilinx では今のところ正しく動かないとの情報は見つけていないので、うまく使えるかどうか試してみようと思います。(このところ本業に追われて本件なかなか進んでおりません) -- [武内(管理人)]