uchan が 2021年09月26日10時26分37秒 に編集
チャタリングの検証を追加
本文の変更
FPGA でスイッチ入力を受け取るときに、そのまま受け取るのではなく、reg を経由すると安定することを実験で確かめました。 ## スイッチ接続回路 今回使った FPGA ボードは [Tang Nano](https://tangnano.sipeed.com/en/) で、スイッチは次の回路によって FPGA に接続されています。 ![Tang Nano のスイッチ接続回路](https://camo.elchika.com/bb9a0ae65bf49ce8899e868178ef9b86863a38b7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f36366430356363662d633864382d343437322d613765342d366564316331613334656364/) 上記の回路図は [Tang Nano の回路図](http://dl.sipeed.com/TANG/Nano/HDK/Tang-NANO-2704(Schematic).pdf) から抜粋しました。チャタリングを緩和するために 0.1μF のコンデンサと 10kΩ の抵抗が接続されています。 ## 動作の様子 Tang Nano には赤・青・緑の 3 色フルカラー LED が搭載されていて、各色の ON/OFF を切り替えることで 2^3 = 8 通りの色を作り出せます。今回作った実験プログラムは、スイッチを 1 回押す毎に、LED の点灯状態が「緑→青緑→青→赤→赤緑→赤青→白→消灯」と切り替わることを意図しています。 reg 無し版の動作 @[youtube](https://www.youtube.com/watch?v=SsHKBSELGqI) reg を使わない場合、スイッチ B を 8 回クリックしても点灯状態が 1 巡しません。1 巡しないというか、1 回のクリックが複数回のクリックに誤認識されているように見えます。そして、押したときだけ反応して欲しいのに、離したときも反応していますね。とても不安定です。 reg 有り版の動作 @[youtube](https://www.youtube.com/watch?v=OfWtmWXYZWQ) reg を経由してスイッチ信号を扱うようにした場合、意図通り 8 回のクリックで 1 巡するようになりました。かなり安定しています。 ## 実験プログラム 実験に用いた Verilog プログラムを紹介します。まずは reg を使わないバージョンです。 ```Verilog 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 を使うバージョンを示します。 ```Verilog 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 無し版の回路](https://camo.elchika.com/1a24f3bcbe8ffea64d8d45c786e42caf1b536eff/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f32306565656234312d643437332d343466302d383266312d623039643332653539313637/) reg 無し版では、スイッチ入力が NOT ゲートを通しただけで直接利用されていることが分かります。 ![reg 有り版の回路](https://camo.elchika.com/b8be79bd6d67a1561d3b052f9ff2c9d4b7dcd64c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f30666266383461662d386234392d346533642d393333662d356637373964383737303932/) reg 有り版では、スイッチ入力の後ろに D-FF が生成されました。 ## メタステーブル FPGA に詳しいわけではないのですが、これはおそらく「メタステーブル」という状態が発生しているのだと思います。非同期信号を同期回路で扱う場合に発生しがちな状態だそうです。 メタステーブルは reg を挿入することで緩和できるそうで、今回のスイッチ入力もバッチリ改善しました。ただ、挿入する reg が 1 つで足りる保証は無いようで、場合によっては 2 個、3 個と挿入する必要があるとのことです。今回は 1 個で十分でしたが、不安定なら reg を増やしてみたら如何でしょうか?
[Tang Nano の開発元が提供するサンプルプログラム](https://github.com/sipeed/Tang-Nano-examples/blob/master/example_led/led_prj/src/led.v) ではスイッチ入力をそのまま利用しているのですが、これはリセット信号だからなんとかなっているものの、一般のスイッチ入力では通用しないことが分かりました。
[Tang Nano の開発元が提供するサンプルプログラム](https://github.com/sipeed/Tang-Nano-examples/blob/master/example_led/led_prj/src/led.v) ではスイッチ入力をそのまま利用しているのですが、これはリセット信号だからなんとかなっているものの、一般のスイッチ入力では通用しないことが分かりました。 ## チャタリング 2021/09/26 追記 メタステーブルではなく単なるチャタリングではないか、という指摘をいただきました。その可能性も気になっていたところではありましたので、実際に波形を見てみました。見てびっくり、とても汚い波形だったのでここで紹介します。 先に紹介したスイッチの回路の IOB3B 端子の電圧を観測したところ、次の波形を得ました。 ![スイッチを押したときの電圧変化](https://camo.elchika.com/4b01208481cd7ec6f1a08d4e5dbd3882ee9b4f9d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f62316265353934662d663463622d343730392d626164652d656463376230353439666230/) スイッチを押すと電圧が下がる回路ですので、トリガは立ち下がりエッジを検出する設定とし、左から 3 メモリの位置(0.3μs)に置きました。縦軸は 1 メモリ 1V、GND レベルは下から 2 メモリの設定です。ボタンを押していないときは 3.3V であることが分かります。 ボタンを押すと 3.3V から 1.2V 程度まで一気に下降した後、1.8V 付近まで揺り戻しがあり、そこから細かい上下を繰り返しながら 0.1μs ほどで GND レベルに落ちています。GND に落ちた後もそこで止まらず、ゆらゆらと電圧が変動しています。完全に落ち着くのに 1μs 程度かかるようです。 当初、私は次のような波形になると予想していたので、実測波形は予想外の形でした。 ![予想していたスイッチ信号](https://camo.elchika.com/4238299bac91dcce35bfa291e1a18617ada54e93/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f35313866643035392d626539352d346530302d616538622d653432626563616662623662/) 先に示した実測波形は、何度かボタンを押して波形を観測した中でもよく出てくる形です。次に示すのは、数割ほどの頻度で見られた特徴的な波形たちです。 ![目立った実測波形その1](https://camo.elchika.com/c1b5ac18b35b24cd1272de775c477b6ee9c6afa2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f65633632356439382d653739362d343962642d613861612d336435346364366536353536/) ![目立った実測波形その2](https://camo.elchika.com/09c48b9e7aa4e17db6cb9808c09944726c96c326/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f65663063343438362d313532312d343262352d613736662d376437626531306666306636/) スイッチの立ち下がりエッジで反応するようにプログラムしたため、上記の波形では複数回のスイッチ入力として処理されるのも納得です。 このままでもチャタリングの証拠が十分に掴めたわけですが、ついでにもっと細かく波形を見てみることにします。先ほどより 10 倍の分解能(横軸を 1 メモリ 10ns)で観測してみた結果をいくつか示します。 ![詳細な実測波形その1](https://camo.elchika.com/1282a877aff7b8fd65f8853433d53850a580ef9e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f39306235343963372d376462352d343036372d393338652d646439386462633235356566/) 「詳細な実測波形その1」は多く見られた波形です。次に、数割ほどの頻度で発生した目立った波形を示します。 ![詳細な実測波形その2](https://camo.elchika.com/8695d9a81056353c8cea98c61966d34677ff7300/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f38343933643631662d383537302d343331392d393666302d633535316236386138333235/) FPGA 視点では高速に何度もスイッチが押されたのと区別ができませんよね。 ![詳細な実測波形その3](https://camo.elchika.com/52cb0a89e52a614e57c1aef0d01e1a90933135f4/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f64356333363166392d623136662d346665312d623264392d663138336466623665663565/) この波形なんて、瞬間の電圧が 6V を超えていて、チャタリングどころか、FPGA に損傷を与えないかどうかの点でひやひやしてきました。 以上、チャタリングの検証でした。