Artix-7からUARTで通信しようとしていた

今のところ制御できていません。「A」と10回だけ出力しようとしたら「A」が無限に出力されてターミナルが埋まるし、「Hello World」と出力しようとしたら「・」が返ってきた。どういうこと? シミュレーションはうまくいってそうに見えるんだけど……。まあVerilog歴1日なので暖かく見守ってください。今日で2日か。

シミュレーションで出てきた波形
様子です。
今よーく見てみたら出力の形がおかしい気がするが、気のせいかもしれない。

久しぶりに書いたコードが全く自分の思ったように動かず、どうしていいのか、何がおかしいのか、あるいは何がわからないのかもわからないという焦りを感じている。直せるという自信があれば特に苦にはならないが、五里霧中だとそういえばこれくらいドロドロした気分になるもんだったな。ソフトウェア技術者が初心を思い返すには別のパラダイムの言語を学ぶんじゃなくて(しばらくやってたらたいていのパラダイムには触れたことくらいあるでしょう?)ハードウェアに挑戦したらよいのではないか。

何はともあれ、プログラミングで重要なことは「とにかく手を動かす」だと思っている。高速にトライアンドエラーを回すためには、手を動かした結果が見える必要がある。つまり、FPGA上で何かを計算したその結果を読み取れる必要があるということだ。そのためにはFPGAの中から外に対して情報を送信できる、信頼できる通信経路が必要だ。今のところ使える通信方法は4つのLEDだけなのだが、LEDを多色に点滅させて計算結果をエンコードするのは論外だろう。このLEDは意外と眩しいので目が潰れる。

というわけで、USB-UARTを通してホストのPCとシリアル通信をしたい。それができれば複雑な結果をPCに送ることができるので、例えばbrainfxxkのインタプリタを実装したり、あるいはレンダリングした画像ファイルを送ることもできるのではないか。というわけで次のチャレンジはUART、と決めたはいいものの、手持ちの本には載っていないようだ。まあ写経ではあまり意味がないので、これもまた一興と自力で実装しようとしたのだが、思っていたよりも難しい。

調歩同期方式についてはとりあえずここを読んでいた。

synapse.kyoto

通信方法については結構わかりやすい。アイドル状態では常に1を出力しておき、データが来たらstart bitとして0を出力し、データ(たいてい8bit)を下の桁から順に出力し、終わったらstop bitを出力する。stop bitの長さには1個、1.5個、2個と少し幅があるようだ。1.5個ってなんだよと思ったが時系列なので電圧上下1.5回分の長さということか。

電圧の上げ下げで信号を送るからには、単位時間について送信側と受信側が合意しておく必要がある。1秒に一回上げ下げする信号を送信したのに、受信側が2秒に一回上げ下げされていると思っていた場合、bitのうち二つに一つは落ちてしまう。この合意を得るためには大きく分けて二つの方法がある。信号と同時にその信号を読み取るのに使うクロックも同時に送る方法(クロック同期)と、先に送受信側の双方が合意しておいてそれぞれが自分のクロックを使う方法(調歩同期)だ。前者はクロックを受信側が持っている必要はないが、信号線を2本用意しないといけない。後者は一本でいいが、双方がそれなりに精度のよいクロックを持っている必要がある。ほーん。

UARTはどうやら調歩同期式で送った信号をシリアル・パラレル変換する回路までを指すようなのだが、どうもふんわりとこの通信のこと全体をこう呼んでそうな雰囲気を感じる。

Arty A7にはmicro USBの口がある。ここからUARTでPCと通信できるらしい。そのピンはこの部分だ。

github.com

最初、uart_rxd_outuart_txd_inとどっちがPC -> ArtyでArty -> PCなのかこんがらがっていたが(TXに書き込むと送信、と説明されていたので……)、リファレンスにわかりやすい図が書いてあった。uart_rxd_out(D10)に書き込むとPC側に送ることができるっぽい。

ついでに、一度PC側と繋いでおくと、USB-UARTでPCからArtyに書き込むことによって(Arty側に受信用の回路を何も書いていなくとも)micro USB端子の横のLEDが光る。通信できているのか? とか気になったら適当なソフトウェア(WindowsだとみんなTeraTermを使っているようだ。SSH用だと思ってたがUSBでのシリアル通信にも対応している)で繋いだ後何か書き込んでみると、とりあえずそのソフトウェアがちゃんと起動できているか、またボードが壊れていないかどうかは確認できるだろう。まあ、電源が入って回路を書き込めてる時点でmicro USBの端子を壊してしまったという可能性は消えているのだが。

reference.digilentinc.com

UARTの仕様を考える限り、

  • データを受け取ったらstart/stop bitを前後に足した10bitのレジスタに乗せ、端から1ビット送るごとにシフトしていく
  • 115200Hz(がよく使われているっぽい)になるよう分周して、そのタイミングで出力を切り替える
  • 送信中にどしどしデータを送られても困るので、送信中かどうかを外に伝える
  • データがなくなっていたら(外から追加のバイトが来なかったら)止まる

というようなモジュールがあればよい、と思っている。その部分はできた気がするが、これにデータを順次送りこむ方で失敗したっぽい。

今後大きめのデータを送る際には、データ送信で処理がブロックされるのは嫌なので、来たデータを置いておくメモリ的なものをUART部分に足しておく方がよいだろうか。計算する側はそこに書き込んでいって、UART側はそれをポップしていくとか? でもそうするとそのメモリからあふれた場合のことも考えておく必要があるが……でも回路を書くんなら問題の規模はだいたい予想ついてるはずだし……。

そもそもこんな風にミスるというのはまだ研鑽が足りていないのでは? おとなしくLEDだけで成否が判定できるような状態機械をいくつか書くくらいのステップは踏んだ方がよかったのでは? 本も読みなおした方がいいと思うが……。

まあ今日はもう遅いので寝ます。