リセットについての考察 のバックアップ(No.3)

更新


公開メモ

リセット信号の扱い

FPGA 開発を始めた当初(半年前くらい?)、 リセット信号の取り扱いについてあまり深く考えておらず、 「何となくリセットが掛かりそうな回路」を書いて満足していました。

しかし、レーシングなどについて勉強してからよく考えてみると、 リセットには慎重な扱いが必要であることが分かってきて、 ここらで一度考え直そうと思い立ちました。

参考にした内容

小林芳直著「定本 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 の記述方法に関する注意点も書かれていて、非常に参考になる

同期リセットと非同期リセット

良く知られるように、リセット信号を持つ回路の構成方法には、 非同期リセットと同期リセットがあって、 たとえば ISE の Language Template にも両方の形が収録されています。

同期リセットと非同期リセットのどちらが回路規模的にお得かは、 ASIC に実装するか、どのメーカの FPGA に実装するか、 などにより変わってくるそうです。

ASIC ではリセットを使う回路と使わない回路とでは実際にゲート1つ分の 回路規模と伝達遅延が差として現れるため、どのラッチにリセットを付けて、 どこに付けないかは、回路規模およびパフォーマンスに直に効いてきます。

これに対して(Xilinx の)FPGA では全ての(通常の) FF に同期リセットあるいは 非同期リセット用に使える SET/RESET (FDRSE: 同期) あるいは PRE/CLR (FDCPE: 非同期) 入力が始めから備わってますので、ここに直接リセット信号を繋ぐ限り、 演算器の回路規模や回路遅延への悪影響はありません。 (もちろんリセットネットの配線リソースは消費します)

async_sync.png

ただこれにはいくつか制限があって、以下のような事が考えられます(wp272より抜粋)。

  • 配線リソース
    • 多くの場合、リセットネットにはかなりの量の配線リソースが消費される
    • リセットネットに要求される最大遅延量を満たすために、他の信号ネットの遅延が増加する場合がある
    • リセットネットを張るためにルーティングにかかる時間が増加する
  • ロジックリソース
    • FF に内蔵されたリセット・クリア機能が使えれば余計なロジックは消費されない
    • 1つのスライスに含まれる2つの FF へのリセット線は共有されるため、 2つの FF が異なるリセット信号を持つ場合には1つのスライスに入れられなくなる
    • リセット・セットあるいはクリア・プリセットと、クロックイネーブルとの 信号線の複数が同時にアサートされたときの優先順位はプリミティブに固有なので、 それに沿わない優先順位で HDL を記述すると余計なロジックが挿入されて、 速度や回路規模に悪影響を与えることになる
    • リセット用のロジックのために PAR にかかる時間が増加する
  • FF 自体を削減する、という最適化が働かなくなる
    • FF がリセットを持たない場合には多数の FF を SRL16E を使って実装することで 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コーディング法」でも 同期リセット回路は非同期リセット回路に比べてツールによる最適化が掛かりやすいという話が 紹介されています。

同期リセットで行くべき

リセットは非同期ではなく同期型にしておくのが良いようです。

GSR の利用

上記のように、回路設計においてリセット回路は回路規模およびパフォーマンスを 圧迫する要因になり得ます。

これに対して、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 のこと

コンフィグレーション直後の値と、リセット直後の値とを異なる値にするのは 余程の事情があるときだけだと思うので、それ以外では非同期リセットの代わりに GSR を使うことで大幅にリソースを削減できます。

また、ユーザーロジックの最適化に対する悪影響もないので、 上記非同期リセットの問題点はすべて解消されています。

(メモ)GSR により Block RAM の出力レジスタの値も初期化されるそうです。
(Block RAM の中身については初期化されません)

似たような考え方で

用途の異なるリセット線を福数本使う場合には、GSR だけでは足りなくなりますが、 リセット信号の伝達に BUFG を使うことでルータへの負担を軽くできるのでは ないかと思います。

FPGA の部屋の marsee さんによれば Xilinx のセミナーでも 紹介された手法らしいです。

http://marsee101.blog19.fc2.com/blog-entry-1464.html

リセット信号を BUFG を通して配線することで ネットの遅延も小さくなりますし、ローカルな配線リソースを消費しない分、 他の配線に与える影響も小さくなるはずなので、BUFG が余っていれば試す価値がありそうです。

この場合、やはり回路は同期リセット回路にしておくのが良いのだと思います。

リセット信号のデアサートは慎重に

同期リセットを使う場合にも、(GSR を含めた) 非同期リセットを使う場合にも、 リセット信号のデアサートのタイミングには十分な配慮が必要になります。

リセットの長さ

同期リセットを使う場合、リセット信号がアサートされる時間は(複数ある場合には最長の) クロック周期よりも長い必要があって、そうでないと最低限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 が同期化されていない場合の問題が明らかになります。

  1. rst = 1 が数クロック続いて state == 4'b0001 になる
  2. rst が 0 に変化する時刻は、ネット遅延により、各ビットで少しだけ異なる
  3. 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] };    // ローテート

非同期リセット回路においても、リセットデアサートタイミングのクロック同期は可能で、 その方法については、下記サイトが参考になりました。

http://natu.txt-nifty.com/natsutan/2008/03/fpga_05d1.html

が、少なくとも Xilinx の FPGA では同期リセットの方が 回路の最適化上で格段に有利とのことなので、以下では同期リセットのみ考えます。

複数のクロック信号がある場合

上と同じ考え方でいくと、もし回路が複数のクロック信号を持っているならば、 リセット信号もクロックの種類と同じだけの種類用意して、 必ずクロックに同期したリセット信号で回路を初期化しなければならない ことになります。

同期化したリセット信号を使っていて、それらの信号に不用意に TIG 制約を付けていなければ、同期リセット回路に間違って異なるクロックに同期した リセット信号を入力してしまったりしても、ツールが遅延時間解析で見つけてくれるので、 うっかりミスも防げます。

同期リセットの問題点

結論として、すべてのリセットを同期リセットとして、 クロックに同期したリセットのデアサートを意識的に行うことで、 安全なリセット回路ができあがります。

ただ、実際にこの方針で回路設計を行うと、規模が小さい間は良いのですが、 大きくなるに従って無理が出てきます。

1つには多くのクロックを使う場合、複数のリセットネットの伝達のために 多くの回路要素を消費してしまうことです。

もう1つは大きなリセットネットはネット内の遅延時間が大きくなるため、

  • リセット信号のネット遅延がクロック周波数を制限してしまったり、
  • リセット信号線の最適化のためにルーティング時間が非常に長く掛かったり、
  • リセット信号線の最適化のために他の信号線の遅延時間が長くなったり、

といった悪影響が現れてきます。

対症療法:同期リセット信号ネットの分割

リセットのデアサートのタイミングは、 上で見たように密接に関連する複数の FF の間で揃っていれば良いので、 同期リセット信号ネットを分割することで、上記のような問題を解決することができます。

すなわち、モジュールの入り口まで非同期リセット信号を届けておいて、 必要なモジュールでのみ、同期リセット信号を作って使えば良いことになります。

ただし同期リセットが必要な回路と、そうでない回路を慎重に見極めて、 必要な箇所で確実にリセットの同期回路を挿入しなければならないため、 十分な配慮が必要なことには代わりありません。

また、リセット信号の同期化のために FF を2つも消費することになりますので、 配線リソースを省けるメリットと、FF の消費とを考え合わせて、 慎重に回路規模を見積もることが必要になります。

リセットのデアサートタイミングが問題にならない例

すべてを同期リセット回路で構成し、 それらにクロックに同期したリセット信号を届けることは、 上で見たように非常に大きな負担となります。

そこで、リセットの必要のない部分にはリセットを掛けないという方針を取らざるを得ないようです。

実際、非同期なデアサートがいつでも問題になるかというとそうでもなくて、 例えばバイナリカウンタをゼロからインクリメントする場合や、 初期ステートが無条件で遷移せず、外部信号のアサートを待つような回路、 あるいは始めの2ステートがジョンソンカウンタの無条件遷移になっている場合など、 リセット信号の非同期なデアサートにより問題が生じない回路も多数存在します。

そのような部分にはロジックによるリセット回路を実装せず、 GSR による再初期化で対応すれば、 リセットを実装するためのリソースを大幅に削減できます。

今考えている最善の方針

ということで、現状での最善策としては、

  • すべてのビットについてクロック同期のリセットが必要かどうかを慎重に見極める
  • クロック同期のリセットが必要な回路には、正しくクロックに同期したリセット信号を届ける
  • そうでない部分は 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 に焼き直すとかいう可能性がなければ気にしなくても??? -- [武内(管理人)]

Counter: 57357 (from 2010/06/03), today: 1, yesterday: 12