シリアル通信をハードウェアレベルで実装した

これは東京高専プロコンゼミ① Advent Calendar 2019 20日目およびOS-CPU Advent Calendar 2019 20日目の記事です。

遅刻しました、ごめんなさい!! そして予定していたテーマからも変わっています。。。お許しを。

言い訳

もともとこの記事のネタは「EFI ELFローダーを作る」の予定でした。今流行りのzen言語を使って作るぞ〜とウキウキしていたのですが、zen言語(zig言語)でサポートされているUEFI APIに誤りや未実装箇所があることが判明したためまずはそちらを修正・追加することから始めていまして時間がかかってしまっています。 一応進捗として、zig言語にPRを送って見事マージされました。やったね。

シリアル通信とは

シリアル通信とは一本の伝送路で一つずつビットを送ることで行うエレクトリカルコミュニケーションです。 シリアル通信といってもいろいろなものがありますが、私が実装したのはUART(Universal Asynchronous Receiver/Transmitter)です。UART以外には、SPIとかEthernetとかUSB(Universal Serial Bus)などもシリアル通信の一種です。

ここでAとBという2つのコンピュータでUART通信することを考えると、Aの送信ポート(TX)とBの受信用ポート(RX)、Aの受信ポート(RX)とBの送信ポート(TX)の計2本を接続する必要があります(実際にはGNDも接続しますが)。 2本の送受信用ケーブルがあるので全二重通信というように呼びます。

UART通信をする際には以下の項目について通信する2つのコンピュータ間で共通の設定をする必要があります。

  • baud rate
  • stop bit
  • data bit
  • parity
  • hard ware flow control

私は以下のように設定しました。

  • 115200bps
  • 1bit
  • 8bit
  • none
  • none

作り方

ソフトウェアでUARTを使うときには設定用レジスタにパラメータをいれて割り込み設定して〜ってかんじだと思いますがハードウェアでやるにはもうちょっといろいろ考える必要があります。 大変なことの一つとして、UARTはクロックを共有しない非同期通信なので位相のずれを考慮する必要があります(周波数は、事前に合わせてあるものとします)。

ここでは概要だけ説明します。

まず送信側ですが、これはやるだけです。FPGAのクロックをUARTのボーレートに分周してあとはそれに合わせてスタートビットとデータとストップビットを送るだけです。 次に受信側ですが、こちらはUARTのボーレートより何倍か早めの周波数でRXを見てあげて、複数回0がきたらそれをスタートビットと認めあとは読んでいく感じです。

f:id:yuhki0223:20191225001619p:plain 送信の様子

f:id:yuhki0223:20191225001632p:plain 受信の様子

すごく適当ですが、アドベントカレンダーなので許してください。 実装したものをGitHubに公開してあります。興味のある人は読んでみてください。

github.com