編集履歴一覧に戻る
uchanのアイコン画像

uchan が 2021年08月18日23時14分35秒 に編集

エコーバックが遅れる理由と対策を追記

本文の変更

本記事では [Tang NanoでuartのIPコアを動かした件](https://qiita.com/yoshiki9636/items/cabcd0c62ea97472b51c) という記事に登場する Verilog コードの理解を目指します。 ## 概要 理解対象の記事は、Tang Nano という FPGA デバイスに UART 通信を行うための IP コアを組み込み、エコーバック処理を実現しています。記事に登場する Verilog コードが全然分からなかったので、タイミングチャートを用いて理解をしようというのが、本記事のゴールです。 理解が難しい原因は、Gowin の UART IP コアのリファレンス文書の記述が不十分なことと、Verilog コードに登場する各種信号線が相互に依存しあっていることでした。UART IP コアの動作がばっちり分かっている前提で Verilog コードを読めば理解できたのかもしれませんが、どちらも分からない状態で読解しようとして苦労しました。最終的には理解できた(気がする)ので、解説を試みます。 ## 1 バイト受信時 まずは 1 バイトだけ受信した際のタイミングチャートを示します。黄色で示したタイミングで 1 バイトを読み出しています。 ![](https://camo.elchika.com/759cb70301e5add611931b56b5f44a5f510f9c4c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f33653261353539612d323336662d343562312d616166302d383235303037656264653434/) radr は UART IP コアのレジスタアドレスを示しており、0 なら RBR、5 なら LSR を読み取ります。RBR は受信バッファレジスタで、受信した 1 バイトを読み出せます。LSR はラインステータスレジスタで、各種のステータスフラグを集めたレジスタです。ビット 0 は RxRDY という名前で、1 なら RBR に受信データが格納されていることを示します。 上記のタイミングチャートは、3 クロック目の後付近(rdata[0] が 1 になる少し前)に 1 バイトの受信が行われたという想定で描いています。1 バイト受信されると LSR.RxRDY が 1 になるため、rdata[0] が 0→1 に変化します。すると、その後 rdd が 1 になり、その影響で radr が 0 になるため、RBR からデータが読み出される、という仕組みです。RBR からデータが読み出されると LSR.RxRDY は 0 に戻ります(と理解しているのですが、間違っていたらご指摘ください)。 このタイミングチャートには載せていませんが、読み出されたデータは直後に THR(送信保持レジスタ)に書かれ、UART から送信されます。つまり、エコーバックですね。 ## 2 バイト受信時 次に連続で 2 バイトを受信した際のタイミングチャートを示します。 ![](https://camo.elchika.com/fd719175a60794e3cfc084b4b38f40a77c0652cb/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63333936313234302d643365342d346361652d396632662d3965396366383634616562342f65303135373664362d363635352d343839372d623732322d626235336532376165653339/) 現実に連続して 2 バイトを受信することはあり得るでしょうか?UART IP コアは 50MHz で動作する一方、UART の通信速度はそこまで速くありません。せいぜい 115.2kbps 程度ですから、バイトとバイトの間隔は 4340 クロックほど空くことになります。ですので、このタイミングチャートは単なる思考実験のように見えるかもしれませんね。 しかし、複数のバイトが UART IP コア内の FIFO に貯まっているケースはあり得ます。受信データの有無をポーリングにより調べる設計では、ポーリング間隔が長ければ、RBR を読み出してもまだ LSR.RxRDY が 1 のまま、というケースはあるでしょう。

-

上記のタイミングチャートで考える限り、このように連続して受信するケースも上手く動きそうです。

+

上記のタイミングチャートで考える限り、このように連続して受信するケースも上手く動きそうです。 ## エコーバックが遅れる理由 理解対象の記事に書かれていますが「適当にキーボードを打つと一つ遅れてエコーバックしてくる」のです。手元で検証し、記事の著者と議論の結果、原因が推測できましたのでここに記します。 端的に言えば Gowin の UART IP コアの仕様(あるいはバグ)によって、LSR.RxRDY が 1 になった後に RBR を読み出すと、1 回目は必ず前回受信したデータが読み出される、ということです。RBR をもう一度読むと、今受信したデータ(LSR.RxRDY を 1 にした要因となった受信データ)が読めます。 ということで、LSR.RxRDY が 1 になった後に 1 バイトを空読みすることで、受信データを正しく読めます。