FPGA でスイッチ入力を受け取るときに、そのまま受け取るのではなく、reg を経由すると安定することを実験で確かめました。
スイッチ接続回路
今回使った FPGA ボードは Tang Nano で、スイッチは次の回路によって FPGA に接続されています。
上記の回路図は Tang Nano の回路図 から抜粋しました。チャタリングを緩和するために 0.1μF のコンデンサと 10kΩ の抵抗が接続されています。
動作の様子
Tang Nano には赤・青・緑の 3 色フルカラー LED が搭載されていて、各色の ON/OFF を切り替えることで 2^3 = 8 通りの色を作り出せます。今回作った実験プログラムは、スイッチを 1 回押す毎に、LED の点灯状態が「緑→青緑→青→赤→赤緑→赤青→白→消灯」と切り替わることを意図しています。
reg 無し版の動作
reg を使わない場合、スイッチ B を 8 回クリックしても点灯状態が 1 巡しません。1 巡しないというか、1 回のクリックが複数回のクリックに誤認識されているように見えます。そして、押したときだけ反応して欲しいのに、離したときも反応していますね。とても不安定です。
reg 有り版の動作
reg を経由してスイッチ信号を扱うようにした場合、意図通り 8 回のクリックで 1 巡するようになりました。かなり安定しています。
実験プログラム
実験に用いた Verilog プログラムを紹介します。まずは reg を使わないバージョンです。
module Counter(
input sys_clk,
input rst_n_async,
input swb_n_async,
output [2:0] led_n
);
reg [2:0] led;
assign led_n = ~led;
always @(negedge swb_n_async or negedge rst_n_async) begin
if (!rst_n_async)
led <= 3'd0;
else
led <= led + 3'd1;
end
endmodule
swb_n_async
はスイッチ入力を受け取る信号名です。回路図の IOB3B に繋がっています。_n
は信号が論理反転していることを、_async
はスイッチが非同期入力であることを表します。ちなみに rst_n_async
も同様のスイッチ入力で、こちらは回路のリセット信号として使っています。
led
はフルカラー LED です。led[0]
が緑、led[1]
が青、led[2]
が赤に接続されています。negedge swb_n_async
のときに LED の点灯状態を切り替えるようにしているので、スイッチを押したときには切り替わり、離したときは何も起きないで欲しいです。しかし、現実はそうはなりませんでした。
次に reg を使うバージョンを示します。
module Counter(
input sys_clk,
input rst_n_async,
input swb_n_async,
output [2:0] led_n
);
reg rst_n;
reg swb_n;
always @(posedge sys_clk) begin
rst_n <= rst_n_async;
swb_n <= swb_n_async;
end
reg [2:0] led;
assign led_n = ~led;
always @(negedge swb_n or negedge rst_n) begin
if (!rst_n)
led <= 3'd0;
else
led <= led + 3'd1;
end
endmodule
非同期入力である swb_n_async
を、クロックの立ち上がりで swb_n
に書き込み、以降の部分では swb_n
を使うようにしました。その他は reg 無し版と同じです。
生成される回路
reg 無し版と reg 有り版のプログラムを論理合成して生成される回路を示します。
reg 無し版では、スイッチ入力が NOT ゲートを通しただけで直接利用されていることが分かります。
reg 有り版では、スイッチ入力の後ろに D-FF が生成されました。
メタステーブル
FPGA に詳しいわけではないのですが、これはおそらく「メタステーブル」という状態が発生しているのだと思います。非同期信号を同期回路で扱う場合に発生しがちな状態だそうです。
メタステーブルは reg を挿入することで緩和できるそうで、今回のスイッチ入力もバッチリ改善しました。ただ、挿入する reg が 1 つで足りる保証は無いようで、場合によっては 2 個、3 個と挿入する必要があるとのことです。今回は 1 個で十分でしたが、不安定なら reg を増やしてみたら如何でしょうか?
Tang Nano の開発元が提供するサンプルプログラム ではスイッチ入力をそのまま利用しているのですが、これはリセット信号だからなんとかなっているものの、一般のスイッチ入力では通用しないことが分かりました。
チャタリング
2021/09/26 追記
メタステーブルではなく単なるチャタリングではないか、という指摘をいただきました。その可能性も気になっていたところではありましたので、実際に波形を見てみました。見てびっくり、とても汚い波形だったのでここで紹介します。
先に紹介したスイッチの回路の IOB3B 端子の電圧を観測したところ、次の波形を得ました。
スイッチを押すと電圧が下がる回路ですので、トリガは立ち下がりエッジを検出する設定とし、左から 3 メモリの位置(0.3μs)に置きました。縦軸は 1 メモリ 1V、GND レベルは下から 2 メモリの設定です。ボタンを押していないときは 3.3V であることが分かります。
ボタンを押すと 3.3V から 1.2V 程度まで一気に下降した後、1.8V 付近まで揺り戻しがあり、そこから細かい上下を繰り返しながら 0.1μs ほどで GND レベルに落ちています。GND に落ちた後もそこで止まらず、ゆらゆらと電圧が変動しています。完全に落ち着くのに 1μs 程度かかるようです。
当初、私は次のような波形になると予想していたので、実測波形は予想外の形でした。
先に示した実測波形は、何度かボタンを押して波形を観測した中でもよく出てくる形です。次に示すのは、数割ほどの頻度で見られた特徴的な波形たちです。
スイッチの立ち下がりエッジで反応するようにプログラムしたため、上記の波形では複数回のスイッチ入力として処理されるのも納得です。
このままでもチャタリングの証拠が十分に掴めたわけですが、ついでにもっと細かく波形を見てみることにします。先ほどより 10 倍の分解能(横軸を 1 メモリ 10ns)で観測してみた結果をいくつか示します。
「詳細な実測波形その1」は多く見られた波形です。次に、数割ほどの頻度で発生した目立った波形を示します。
FPGA 視点では高速に何度もスイッチが押されたのと区別ができませんよね。
この波形なんて、瞬間の電圧が 6V を超えていて、チャタリングどころか、FPGA に損傷を与えないかどうかの点でひやひやしてきました。
以上、チャタリングの簡単な検証でした。
手元にあったタクトスイッチで詳細な検証をしたので、興味があればこちらもどうぞ タクトスイッチのチャタリングを検証
投稿者の人気記事
-
uchan
さんが
2021/09/18
に
編集
をしました。
(メッセージ: 初版)
-
uchan
さんが
2021/09/18
に
編集
をしました。
(メッセージ: タグを追加)
Opening
AoiSaya
2021/09/19 -
uchan
さんが
2021/09/19
に
編集
をしました。
(メッセージ: 論理合成結果の回路図を追加)
Opening
ozwk
2021/09/21 -
uchan
さんが
2021/09/26
に
編集
をしました。
(メッセージ: チャタリングの検証を追加)
-
uchan
さんが
2021/09/26
に
編集
をしました。
(メッセージ: タグを追加)
Opening
idt12312
2021/09/26 -
uchan
さんが
2021/09/27
に
編集
をしました。
(メッセージ: チャタリング検証記事へのリンクを追加)
ログインしてコメントを投稿するFPGAを使う全員に知っておいてほしいトピック。
自分は会社で習ったのでこういうところに投稿した方がいいことに気づけませんでした。こういう記事が増えるといいですね。
ご参考までに、regですが、一般的には2個以上入れます。クロック周波数が高い場合は3個以上入れることもあります。regの間には極力回路は入れません。
こちらの記事を参考になさってください。
https://www.macnica.co.jp/business/semiconductor/articles/intel/2130/
メタステーブルではなく、単なるチャタリングかと思います。
IOB3B 端子の電圧を数ms~数十msの時間幅でオシロで測定するとチャタリング波形が見えるかもしれないです。チャタリングは機械的な振動なので、ms以上のオーダーでの振動になりそうです。
オシロで1目盛り10nsで測定したときの振動は0.1nFと配線のインダクタ成分で共振している波形じゃないかと思いました。それにしても、瞬間的にFPGAを壊しそうなくらいの電圧がでていることがあることには驚きました。