リセットについての考察 のバックアップ差分(No.16)
更新- バックアップ一覧
- 現在との差分 を表示
- ソース を表示
- バックアップ を表示
- 電気回路/HDL/リセットについての考察 へ行く。
- 追加された行はこの色です。
- 削除された行はこの色です。
[[公開メモ]]
#contents
* リセット信号の扱い [#nf87df7f]
FPGA 開発を始めた当初(半年前くらい?)、
リセット信号の取り扱いについてあまり深く考えておらず、
「何となくリセットが掛かりそうな回路」を書いて満足していました。
しかし、レーシングなどについて勉強してからよく考えてみると、
リセットには慎重な扱いが必要であることが分かってきて、
ここらで一度考え直そうと思い立ちました。
ところが、調べれば調べるほどいろんなことが出てきて、
ちょっと泥沼状態です・・・
* 参考にした内容 [#y1087302]
:小林芳直著「定本 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/sim0058_10.html|
Simulating VHDL (Xilinx) 後から見つけた情報なので、まだ内容を取り込めていません。
* 同期リセットと非同期リセット [#f67ab6da]
良く知られるように、リセット信号を持つ回路の構成方法には、
非同期リセットと同期リセットがあって、
たとえば ISE の Language Template にも両方の形が収録されています。
同期リセットと非同期リセットのどちらが回路規模的にお得かは、
ASIC/FPGA/CPLD どれに実装するか、またどのメーカの FPGA に実装するか、
などによっても変わってくるそうです。
ASIC ではリセットを使う回路と使わない回路とで 実際にゲート1つ分の
回路規模と伝達遅延が差として現れるため、どのラッチにリセットを付けて、
どこに付けないかは、回路規模およびパフォーマンスに直に効いてきます。
これに対して(Xilinx の)FPGA では全ての(通常の) FF に同期リセットあるいは
非同期リセット用に使える SET/RESET (FDRSE: 同期) あるいは PRE/CLR (FDCPE:
非同期) 入力が始めから備わっているので、ここに直接リセット信号を繋ぐ限り、
演算器の回路規模や回路遅延への悪影響はありません。
(もちろんリセットネットの配線リソースは消費します)
&attachref(async_sync.png);
ただこれにはいくつか制限があって、以下のような注意事項が挙げられています。~
([[wp272>http://www.xilinx.com/support/documentation/white_papers/wp272.pdf]]より抜粋)
- 配線リソース・配線遅延
-- 多くの場合、リセットネットにはかなりの量の配線リソースが消費される
-- リセットネットに要求される最大遅延量を満たすために、他の信号ネットの遅延が増加する場合がある
-- リセットネットを張るためにルーティングにかかる時間が増加する
-- リセットネットはしばしば非常に大きなファンアウトを持つので、
その遅延時間がクロック周期の最小値を制限してしまうこともある
- ロジックリソース
-- 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 をリセットする機構は
プリミティブに存在しないため、もし必要であれば初期化用のステートマシンを
組む必要がある
** リセット・セット・クロックイネーブルの優先順位に関する捕捉: [#d3c2ab05]
例えば 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 を入れ替えても同じです。
** 回路規模やネット遅延について [#dc1fd73b]
回路規模に関しては、Xilinx の FPGA では非同期リセットに比べて
同期リセットの方が有利であるという結果が FPGA の部屋 で得られているそうです。
http://marsee101.web.fc2.com/reset_of_fpga.html
Xilinx の「[[デザイン パフォーマンス向上のためのHDLコーディング法>http://japan.xilinx.com/xcell/xl55/jp55xcell_04.pdf]]」でも
同期リセット回路は非同期リセット回路に比べてツールによる最適化が掛かりやすいという話が
紹介されています。
** 同期リセットで行くべき [#t75d3d2b]
したがって、手で記述するリセットは非同期ではなく同期型にしておくのが良いようです。
* GSR の利用 [#m4f6a7c5]
上記のように、回路設計においてリセット回路は回路規模およびパフォーマンスを
圧迫する要因になり得ます。
これに対して、Xilinx の FPGA では GSR をうまく使うことでユーザーロジックによるリセットを軽減し、
回路規模やパフォーマンスを大幅に改善できる可能性があります。
GSR とは Global Set Reset 信号の略で、これをアサートすることにより
全ての FF を (Block/Distributed RAM および LUT に実装されたシフトレジスタ、DCM を除く)
コンフィグレーション直後の値にリセットすることができます。
GSR の動作はちょうど、隠された非同期リセット信号線があると考えれば良くて、
この機能を使うために追加で配線リソースやロジックリソースが消費されることはありません。
GSR を使うには STARTUP_???? というような名前のプリミティブを使うことになります。
???? の部分には FPGA の名前が入るので、Spartan3 であれば STARTUP_SPARTAN3 とか
STARTUP_SPARTAN3A などとなります。
このモジュールには GSR という入力があるので、これを1にすることで全ての FF
に非同期リセットがかかります。
ここで言う FF の初期値とは、Verilog で reg を宣言する際に指定する値です。
LANG:verilog
localparam st_idle = 3'b000;
reg [2:0] state = st_idle; // この st_idle のこと
あるいは INIT 属性などで指定することもできます。
(* INIT="1" *) reg enable;
コンフィグレーション直後の値と、リセット直後の値とを異なる値にするのは
余程の事情があるときだけだと思うので、それ以外では非同期リセットの代わりに
GSR を使うことで大幅にリソースを削減できます。
また、ユーザーロジックの最適化に対する悪影響もないので、
上記非同期リセットの問題点はすべて解消されています。
(メモ)GSR により Block RAM の出力レジスタの値も初期化されるそうです。~
(Block RAM の中身については初期化されません)
*** Altera の FPGA では reg の初期値は使えない? [#z1b97545]
ふnさんからのコメントで、Altera の FPGA では reg
に与えた初期値は一見うまく動作するように見える物の、
低確率で正しく初期化されない場合があることを教えていただきました。
つまり、Altera の FPGA では初期値の必要な reg
は必ずユーザーロジックで初期化する必要があることになります。
http://www.altera.co.jp/literature/hb/qts/qts_qii51007_j.pdf
など Altera の出している文書にも (少なくとも VHDL では) reg
の初期値を指定できるように書かれているため、
「指定してもうまく行かない場合がある」
というのはまずいようにも思うのですが・・・
reg に初期値を与えるというのは
あまり積極的に使われていない機能なのかもしれませんね。
** 似たような考え方で [#n7fe63a0]
用途の異なるリセット線を福数本使う場合には、GSR だけでは足りなくなりますが、
リセット信号の伝達に BUFG を使うことでルータへの負担を軽くできるのでは
ないかと思います。
FPGA の部屋の marsee さんによれば Xilinx のセミナーでも
紹介された手法らしいです。
http://marsee101.blog19.fc2.com/blog-entry-1464.html
リセット信号を BUFG を通して配線することで
ネットの遅延も小さくなりますし、ローカルな配線リソースを消費しない分、
他の配線に与える影響も小さくなるはずなので、BUFG が余っていれば試す価値がありそうです。
この場合、やはり回路は同期リセット回路にしておくのが良いのだと思います。
* リセット信号のデアサートは慎重に [#q078b306]
同期リセットを使う場合にも、(GSR を含めた) 非同期リセットを使う場合にも、
リセット信号のデアサートのタイミングには十分な配慮が必要になります。
** リセットの長さ [#w9012de2]
同期リセットを使う場合、リセット信号がアサートされる時間は(複数ある場合には最長の)
クロック周期よりも長い必要があって、そうでないと最低限1回、
クロックエッジの時点でリセットがアサートされている状況を保証できません。
ただ、この問題はリセット信号を十分長い時間アサートすることで、簡単にクリアできるため、
現実問題としてはあまり注意を払う必要は無いと思います。
** デアサートのタイミングによっては正しく初期化できない [#x00de0de]
デアサートのタイミングが重要、という例を考えてみました。
(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
となる可能性があって、この場合、リセット直後にホットビットが失われてしまい、
回路はまともに動作しません。
同様に、バイナリカウンタを定数値からデクリメントして使うような場合も、
おかしな結果を生む可能性のある例になると思います。
** 非同期リセット回路でも [#udb29070]
非同期リセット回路でもリセットをデアサートするタイミングと
クロックエッジとが重なれば同じ状況が起きると思います。
* クロックのデアサートをクロックエッジに同期する [#ye122eb3]
最も簡単な回避方法は、同期リセット回路へ入力するリセット信号デアサートを
クロックエッジに同期することだと思います。
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] }; // ローテート
非同期リセット回路においても、リセットデアサートタイミングのクロック同期は可能で、
その方法については、下記サイトが参考になりました。
http://natu.txt-nifty.com/natsutan/2008/03/fpga_05d1.html
が、少なくとも Xilinx の FPGA では同期リセットの方が
回路の最適化上で格段に有利とのことなので、以下では同期リセットのみ考えます。
** 複数のクロック信号がある場合 [#a2504aa4]
上と同じ考え方でいくと、もし回路が複数のクロック信号を持っているならば、
リセット信号もクロックの種類と同じだけの種類用意して、
必ずクロックに同期したリセット信号で回路を初期化しなければならない
ことになります。
同期化したリセット信号を使っていて、それらの信号に不用意に TIG
制約を付けていなければ、同期リセット回路に間違って異なるクロックに同期した
リセット信号を入力してしまったりしても、ツールが遅延時間解析で見つけてくれるので、
うっかりミスも防げます。
* 同期リセットの問題点 [#c8ee6451]
結論として、すべてのリセットを同期リセットとして、
クロックに同期したリセットのデアサートを意識的に行うことで、
安全なリセット回路ができあがります。
ただ、実際にこの方針で回路設計を行うと、規模が小さい間は良いのですが、
大きくなるに従って無理が出てきます。
1つには多くのクロックを使う場合、複数のリセットネットの伝達のために
多くの回路要素を消費してしまうことです。
もう1つは大きなリセットネットはネット内の遅延時間が大きくなるため、
-リセット信号のネット遅延がクロック周波数を制限してしまったり、
-リセット信号線の最適化のためにルーティング時間が非常に長く掛かったり、
-リセット信号線の最適化のために他の信号線の遅延時間が長くなったり、
といった悪影響が現れてきます。
* 対症療法:同期リセット信号ネットの分割 [#v5bc6453]
リセットのデアサートのタイミングは、
上で見たように密接に関連する複数の FF の間で揃っていれば良いので、
同期リセット信号ネットを分割することで、上記のような問題を解決することができます。
すなわち、モジュールの入り口まで非同期リセット信号を届けておいて、
必要なモジュールでのみ、同期リセット信号を作って使えば良いことになります。
ただし同期リセットが必要な回路と、そうでない回路を慎重に見極めて、
必要な箇所で確実にリセットの同期回路を挿入しなければならないため、
十分な配慮が必要なことには代わりありません。
また、リセット信号の同期化のために FF を2つも消費することになりますので、
配線リソースを省けるメリットと、FF の消費とを考え合わせて、
慎重に回路規模を見積もることが必要になります。
クロックの種類が多くない場合で、かつ BUFG
が余っている状況では同期リセット信号線を BUFG 経由で配信すれば、
巨大なファンアウトを持つリセット信号も最小の遅延時間で伝達することが
可能ですので、以下のような難しいことを考えなくても良い場合もあるかもしれません。
* リセットのデアサートタイミングが問題にならない例 [#v9e45556]
すべてを同期リセット回路で構成し、
それらにクロックに同期したリセット信号を届けることは、
上で見たように非常に大きな負担となります。
そこで、リセットの必要のない部分にはリセットを掛けないという方針を取らざるを得ないようです。
実際、非同期なデアサートがいつでも問題を引き起こすかというとそうでもなくて、
例えばバイナリカウンタをゼロからインクリメントする場合や、
初期ステートが無条件で遷移せず、外部信号のアサートを待つような回路、
あるいは始めの2ステートがジョンソンカウンタの無条件遷移になっている場合など、
リセット信号の非同期なデアサートにより問題が生じない回路も多数存在します。
そのような部分にはロジックによるリセット回路を実装せず、
GSR による再初期化で対応すれば、
リセットを実装するためのリソースを大幅に削減できます。
* 今考えている最善の方針 [#u897a4fb]
ということで、現状での最善策としては、
- すべてのビットについてクロック同期のリセットが必要かどうかを慎重に見極める
- クロック同期のリセットが必要な回路には、正しくクロックに同期したリセット信号を届ける
- そうでない部分は GSR で再初期化することにより対応する
というようなことを考えています。
ただ、調べてみると GSR を手動リセットに使うのは色々手間の掛かる部分もあるようで、
本当にこの方針で良いのか、以下のように考察中です。
* シミュレーションで GSR をエミュレートする方法 [#ya7a7f51]
GSR 信号を有効活用する上でまず壁になりそうなのが、
GSR の動作をシミュレーションで確認するのが大変であるという点になります。
上記のように GSR をユーザー回路からコントロールするには
STARTUP_SPARTAN3 モジュールを使うことになります。
また、Xilinx FPGA ではコンフィグレーション直後の 100ns 程度の間、
自動的に GSR がアサートされ、デバイスはリセット状態を保ちます。
普通に ISE から ModelSim や ISim を起動してデバッグすれば、
ユーザーコードの他に Xilinx の glbl モジュールがインプリメントされ、
シミュレーションの起動から 100 ns 程度の間 GSR がアサートされることで、
この動作がエミュレートされます。
ただ、このようにして GSR がアサートされても、
ビヘイビャーレベルではシミュレーション結果には GSR の動作が反映されません。
すなわち、GSR がアサートされている間にも通常通りレジスタの書き換えが行えてしまいます。
考えてみればそれはそうで、恐らく Verilog 言語には GSR のような
非明示的なリセット回路を記述する構文はないのだと思います。
ではどうするかというと、
http://toolbox.xilinx.com/docsan/xilinx6/books/docs/sim/sim.pdf
の253ページに1つの方法が例示されています。
個々のレジスタに対して特別に GSR に対応するための記述を行っておき、
テストベンチから個々のモジュールに GSR 信号を配信するというものです。
** コード例 [#z377572b]
次の例は非同期リセット信号を使って GSR をドライブし、FPGA 内の全ての FF
を初期値に戻すと共に、非同期リセット信号がデアサートされてから 256
クロック程度経ってからデアサートされる同期リセット信号を生成する回路を書いたものです。
このコードを使って GSR 動作のシミュレーション方法を解説します。
この回路で再リセット時に初期化されなければならない FF は rst および count です。
実機では STARTUP_SPARTAN3A の .GSR 入力に1を立てることでこれらの FF
は初期化されるのですが、
何も特別なことをしなければシミュレーション上では再初期化が働きません。
そこで、GSR による再初期化をエミュレートするために
+ 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 信号線で下位モジュールの GSR_in をドライブする。
ただし、この GSR にはドライブ源がないので、論理合成時には無視されることになる。
LANG:verilog
module project_top #(
parameter COUNTER_BITS = 8
) (
input wire async_rst,
input wire clk,
...
);
wire GSR; // ドライブ源がないため論理合成時には無視されるダミーの信号線
wire sync_rst;
reset_gen reset_gen (
.GSR_in(GSR),
.async_rst(async_rst),
.clk(clk),
.rst(sync_rst)
);
...
endmodule
テストベンチからトップモジュールの project_top.GSR を glbl.GSR でドライブする。
LANG:verilog
module project_top_test;
...
project_top uut (
...
);
assign project_top.GSR = glbl.GSR;
...
endmodule
一応、このような方法で GSR をエミュレートが可能です。
- すべてのレジスタについて if (GSR_in) によるダミーの初期化文を記述しなければならないこと
- 複雑な階層の末端のモジュールまで、テストベンチから GSR 信号を届けなければならず、
その部分の記述も煩雑なこと
の2つは、通常の非同期リセットでも同じなため我慢できるとして、
- reg signal = initial_value; という部分と、if (GSR_in) signal <= initial_value;
の2カ所に初期値を記述しなければならず、DRY ([[Google:Don't repeat yourself]])
の原則に反する~
もちろん、1つ定数を用意してそれを2カ所で使えば原理的には問題ないのだけれど
と言う点については、うまい回避策も思い浮かばず、嫌な感じです。
また、合成時に出る
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.
という警告も気になるところです。
ネット上で検索してもこの警告の意味するところが分かりませんでした。
* GSR 信号の延長 [#o241b7ef]
コメント欄で marsee さんから、GSR 信号線をリセットとして使うと、
リセット状態が解除された時点でクロックが安定している保証がないので
気をつけた方が良いと指摘していただいたので、これについて考えてみました。
まず、Xilinx の [[ug332>http://japan.xilinx.com/support/documentation/user_guides/j_ug332.pdf]]
の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 さんからさらに書き込みをいただきました。
コンフィグレーションをパラレルで高速に行った場合には、
外部クロックの起動時間よりもコンフィグレーションに掛かる時間の方が短くなる可能性があって、
その場合には、実際に追加で数ミリ秒の待ち時間が必要になることもあるのだそうです。
回路設計の前に、まじめに発振子のスペックと、
コンフィグレーション時間とを比べてみる必要がありそうです。
** ユーザーロジックを使ってさらに延長することを試みる [#pf75b915]
こういうことが問題になる背景には、GSR がアサートされている限り通常の FF を使えないため、
例えば「DCM の LOCKED がアサートされてから 200 クロック後にリセットを解除する」
というようなロジックを組むのに工夫がいるという点があります。
以下では通常の FF の代わりに LUT をシフトレジスタとして使うことでカウンタを構成し、
DCM の LOCKED 後に GSR をリリースするタイミングを測ることを考えました。
** GSR 中にも動作するカウンタ回路 [#i3cac811]
次の回路により 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
) GSR_counter_l (
.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
2つ繋げれば、100 クロックに一度 1 を出力するような回路も簡単に作れます。
LANG:verilog(linenumber)
// それぞれ 10 clk / 100 clk に一度 1 になる
wire pulse10, pulse100;
srl16_pulser #(10) pulser1 ( .clk(clk), .en(1), .pulse(pulse10) );
srl16_pulser #(10) pulser1 ( .clk(clk), .en(pulse10), .pulse(pulse100) );
この回路は LUT を1個だけしか使わないので、
非常に省スペースのカウンタとして、結構使い手があるかもしれません?
問題はカウンタのリセット回路がないことですが・・・
** より安全なカウンタ回路 [#c616c910]
上記のカウンタは 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 を延長する [#bc99ad7a]
上記 srl16_pulser32 を使い、async_rst
が下りてから EXTENTION x 32 クロック後に GSR を解除するコード
(実際には GSR_out を上位モジュールで STARTUP_SPARTAN3 に入れる)を書いてみました。
ここで EXTENTION には 15 以下の整数を指定します。
LANG:verilog(linenumber)
module gsr_gen #(
parameter EXTENTION = 10 // x 32 クロックだけ延長する
) (
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)
);
// GSR を EXTENTION x 32 クロック延長する
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 & (pulse32| !GSR_active) ), // Clock enable input
.D(!GSR_active) // SRL data input
);
// GSR 出力
assign GSR_out = async_rst | ( GSR_active && gsr_extended );
endmodule
ミリ秒単位の長い遅延時間が必要となる場合には、
srl16_pulser32 を重ねて使えば良いはずです。
例えば基本クロックが 100 MHz の時、4つ重ねると約 10ms の周期が得られますので、
EXTENTION パラメータを 5 とすることで 50ms の遅延が得られます。
注意点として async_rst が続けざまに on/off された場合の動作を記述しておきます
+ async_rst を離してから GSR がリリースされる前に再度 async_rst をアサートした場合、
GSR の待機カウンタはリセットされません。2度目のリセット中にカウンタが止まっていたかの
ように振る舞い、その後、動き出したカウンタが既定値に達すると GSR が解除されます
+ GSR 解除の後 16 クロック以内に再度 async_rst がアサートされた場合、
待機カウンタがまだクリアされていないため、async_rst が離されてから
最短で次のクロック、最長でも 32 クロック以内に GSR がリリースされてしまいます
** 同期リセット信号を生成 [#p4f5b201]
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
** 使い方 [#s2b2083f]
使うときは、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 をそのまま、
あるいはこれをローカルに別のクロックに同期したリセット信号を作成し、
同期リセット回路に入力することで実装しようと考えています。
同期リセット信号の配信にはグローバルな高速配線リソースを使っているので、
ローカルな配線リソースを圧迫することはなく、また、
ネット遅延によりクロック周期を圧迫することも少なくできると思います。
* Post-PAR シミュレーションでは [#ibe27ab1]
上で考察したとおり、
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;
* 注意点 [#g7196ae7]
ここまで読んでいただいた上で いまさらなのですが、
まだ実機では試せていませんので、話半分で(!)お願いします(汗
* コメント [#h2be8398]
#article_kcaptcha
**リセットの取り扱いについて [#abaf87de]
>[ふn] (2010-07-03 (土) 12:22:22)~
~
ちょっと、時期を逃してしまいましたが・・・『reg の宣言時に初期値』についてですが、Altera 社のデバイスでは保証しない、と言われました。確かにQuartusのハンドブックにも、できるようなことを書いてありましたが、運が悪いとどちらに転ぶかわからないそうです。~
//
- 重要な書き込みありがとうございます。これは、Altera社のセミナーか何かで得られた情報でしょうか?多くの場合できるけれど、保証はしない、というのはバグを仕様と言い張るように聞こえますが、ベンダーの発表であれば仕方がないですね。注意書きとして本文中に入れさせていただこうと思います。 -- [武内(管理人)] &new{2010-07-03 (土) 19:39:45};
- 日本アルテラに問い合わせた結果、「保証できません」との回答を得ました。リセット信号の解除時にクロックとちょうどよいタイミングではまると、どちらに転ぶかわからないそうです。そのような訳で、リセット信号は同期化したものを使用するように、とのことでした。 -- [ふn] &new{2010-07-04 (日) 11:19:27};
- 実験もしてみましたが、やっぱりアルテラのいう通りの結果となりました。実験はMaxIIを使いましたが、アルテラ社のデバイスは全てに対して適用されるとのことでした。これは2009年の話ですから、今年に入ってから改善・・・それはないかな? -- [ふn] &new{2010-07-04 (日) 11:22:25};
- お返事ありがとうございます。素人目にはリセット信号の解除がクロックに同期しているかどうかで初期値が変化するというのは不思議に思えました。一点確認させていただきたいのですが、これは[[上記で書いているレーシング>#x00de0de]]などとは異なる次元の問題なのですよね? -- [武内(管理人)] &new{2010-07-04 (日) 14:49:58};
- ユーザーリセットやGSRの解除をどのタイミングで行うべきか、という話題と、コンフィグレーションやGSR時にreg宣言時の初期値が反映されるか否か、という話題とを区別して議論したいと思います。reg 宣言時の初期値が(1の時には?)デバイスのコンフィグレーションに反映されないのであれば、かなり重大な問題ですね。そういうデバイスをターゲットとする限り、同期か非同期かに関わらず、何らかのユーザーリセットが必要になるのは間違いないと思います。 -- [武内(管理人)] &new{2010-07-04 (日) 14:52:33};
- 対して、これまでネット上の文献で調べた限り Xilinx のデバイスでは通常の FF に割り当てられたすべての reg は GSR のアサートによって初期値に固定されることが保証されているように読めました。ただ、GSRネットの伝達遅延が規定されていないという点に十分に注意を払って利用しないと、GSR のデアサート時にレーシングの問題が生じるため、(その意味をしっかりと理解できないのであれば)GSR をユーザーリセットに使うことはあまり推奨しないというような記述も目にしました。まだしっかりと実機でテストできていないのですが、書かれている内容は納得のいくものなので、恐らくその通りの動作をするのだろうと期待しています。(どこかに穴がないと良いのですが・・・) -- [武内(管理人)] &new{2010-07-04 (日) 15:09:55};
- 『デアサートのタイミングによっては正しく初期化できない』とは別に考えた方がよいと思います。アルテラでは、『reg の宣言時に初期値』を保証しないというののは、コンフィギュレーションが完了してユーザモードに移る際に、クロックとぶつかるとどう動くかわからない、ということだったと思います。ですので、reg a_reg = 1; と記述したとしても、ユーザモードに入ったときに、この a_reg は "1" であることが保証されない、ということです。もちろん reg a_reg = 0; と記述したとしても、コンフィギュレーション完了後には "0" であるという保証はされない、とのことです。 -- [ふn] &new{2010-07-04 (日) 16:56:10};
- 『reg 宣言時の初期値が(1の時には?)デバイスのコンフィグレーションに反映されない』という問題です。反映されないは言い過ぎですが、反映されない場合がある、ということです。かなりの高確率で反映はされます。 -- [ふn] &new{2010-07-04 (日) 17:07:55};
- 詳細な情報をありがとうございます。とてもよく分かりました。何だか、反映するように作ったはずがうまく動かなかったので仕様の方を替えた、という雰囲気が強いですが本当にうまく動かない場合があるのであれば、ユーザー側は「反映されない」ことを前提に設計するしかありませんね。 -- [武内(管理人)] &new{2010-07-04 (日) 17:50:50};
- ありえない回路ですが、1000ビット程度のシフトレジスタでLSBとMSBのビットを接続した回路を作り、reg [999:0] a_reg = '1; (1000ビットに"1"をセット)と宣言し、リセットなしのalways文で記述しました。温度や電圧に関係なく、30000回に1〜3回くらいは、1000ビット中のどこかのFFが反転するものでした。宣言時にAll"1"だけではなく、All"0"でも試しましたが、結果は同様です。ですので、初期値をセットした場合に反転してしまう確率はかなり低いものですが、初期値を保証できるというレベルではありません。 -- [ふn] &new{2010-07-05 (月) 06:42:52};
#comment_kcaptcha
**リセットの取り扱いについて [#m03f0a3a]
>[marsee] (2010-06-22 (火) 13:34:32)~
~
Xilinxのセミナでは、リセットが必要ないところは使わないようにした方が良いとのことでした。~
XilinxのFPGAは、コンフィギュレーションする時にD-FFをすべて0にしています。よって、初期値が0で良いならばリセットする必要がないわけです。シミュレーションの時に困ることがあると思いますが、それでいいならば、リセットしない方が回路規模や色々なタイミングを満足するために良いとの事でした。~
//
- はい、リセットはできる限り省くのが良いみたいですね。Verilog では[[本文中に(後から)記述した形式で>#m4f6a7c5]] reg の宣言時に初期値を決めれば 0 以外の初期値も可能なように思うのですが・・・正しいでしょうか? -- [武内(管理人)] &new{2010-06-22 (火) 14:05:05};
- 論理合成ツールがXSTの場合は可能だそうです。たぶん、Synplifyなどでは無視されるのだと思います。 -- [marsee] &new{2010-06-22 (火) 15:57:05};
- ちょっと調べてみたところ、おっしゃるとおり Synplify ではだめなようでした。http://tinyurl.com/2c22jfw によれば /* synthesis xc_props="INIT=S" */ というような属性で1にするか0にするかは選べるみたいですが、ベクター全体が1か0になってしまうため、適当な定数を初期値とするのは難しいようですね。 -- [武内(管理人)] &new{2010-06-22 (火) 20:02:19};
- と思ったら、http://tinyurl.com/25gwa2h では Synplify でも初期値を使えると書いてありますね。上の記述は 2005 年で、こちらは 2009 年なのでこの間に対応したと言うことかもしれません。が、この記事でも初期値は使えるツールと使えないツールがあるため、ポータビリティが低いという雰囲気になっています。同じ記事で Quartus でも使えると書いてありますので、最新版のツールを使う限りターゲットが FPGA であれば大抵大丈夫なのかもしれません。後から ASIC に焼き直すとかいう可能性がなければ気にしなくても??? -- [武内(管理人)] &new{2010-06-22 (火) 20:10:08};
- XSTもSynplifyも使える環境にあるので、後で本当はどうなのか確かめてみます。私は少なくともうちののシステムはリセットをしようと思っています。理由はクロックが電源ONから安定とは限らないからです。 -- [marsee] &new{2010-06-23 (水) 09:21:56};
- GSRを使う文脈をいろいろ考えてはみているのですが、クロックが安定するまで待つ方法を始め、おっしゃる通りいろいろ難しいところがありそうですね。今はそのあたりをシミュレーションで確かめる方法を検討しているのですが、STARTUP_SPARTAN3A の GSR ピンを上げてもレジスタ値は初期化されていないようで・・・何か根本的に違うことをしているみたいです? -- [武内(管理人)] &new{2010-06-23 (水) 10:43:00};
- ビヘイビャーレベルのシミュレーションで GSR の動作を正しくエミュレートするには http://toolbox.xilinx.com/docsan/xilinx6/books/docs/sim/sim.pdf の253ページにあるように、個々のレジスタに対して特別な記述をしなければならないのですね。これはかなり面倒そうです。また GSR はネット遅延が規定されていないので手動リセット回路としては使わない方が良いという記述がちらほらと。これは非同期であることを正しく考慮できるなら気にしなくても良いのかもしれませんが・・・ いずれにせよ、また考え直さないと行けないみたいです。 -- [武内(管理人)] &new{2010-06-23 (水) 12:04:27};
- 「電源オン直後にクロックが不安定な可能性」というのは、水晶発振器自体が電源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] &new{2010-06-29 (火) 09:04:18};
- FPGAをスレーブにしてパラレル通信で高速に書き込んでしまうと、クロックが安定するよりもコンフィグレーションの方が早く終わってしまうこともあるのですね。私の頭にはマスタ SPIでのコンフィグレーションしかなかったので、思い至りませんでした。一応、上記のようにすればGSRでもリセットのデアサートに遅延を入れられるので、いざとなれば数十ミリ秒を待つこともできそうに思えるのですが、まだ実機で試していないのでうまく行ったらまた報告しようと思います。 -- [武内(管理人)] &new{2010-06-29 (火) 09:18:09};
- あれ、マスタ SPI でも FPGA のサイズが小さかったりすると結構危ういですね。10 ms というのが周波数がどの程度の範囲に入るまでの時間なのか、データシートからは分からなかったのですが、この時間をしっかり待とうとするとユーザーロジックでの待機が必要となることが理解できました。 -- [武内(管理人)] &new{2010-06-29 (火) 09:42:10};
#comment_kcaptcha
Counter: 72628 (from 2010/06/03),
today: 1,
yesterday: 0