FPGAでENC28J60を動かしたい

需要不明記事に以下のツイートを添えて

FPGAからUDPパケットを10Gbpsで送信したい、というのは非常に自然な欲求です。ですが、非常に残念なことに、この欲求を実現するには決して安くない金額が必要になります。10G Ethernetに対応したPHYが載っているFPGAボードの値段を見てみたら、4桁で単位がドルでした。ふふってなりますよね、僕はなりました。
もう一つの選択肢としてPHYを単体で購入して外付けする方法があります、これは楽しそうなので200年後くらいにやります。

それはさておき、10Mbpsなら比較的安価に、比較的手軽にFPGAからパケットを送信する方法があります。そうですね、ENC28J60です。SPIで制御可能なEthernetコントローラ!すごい!嬉しい!お手軽!
お手軽そうですね、やっていきましょう。

ja.aliexpress.com

目標:パケット送信

Ethernetケーブルを繋いでWiresharkでパケットが見るのを目標にしましょう。

FPGAとSPI

ENC28J60とFPGAを通信させるにあたって、FPGAにSPI Masterを実装する必要が出てきます。SPIの仕様を確認しましょう。

ja.wikipedia.org

SPIには4つのモードがあるようです。ENC28J60との通信に使うのはこの内のMode 0です、つまりidle時にはSCLKは0, SCLKの立ち上がりでMISOの状態を記録する, SCLKの立ち下がりでMISOに次のデータを出力ということです、
図にするとこう。

f:id:Cra2yPierr0t:20200730160300p:plain
図1 mode0の波形

ENC28J60仕様

次にENC28J60と通信するデータフォーマットを調べましょう。データシートを読みます。

f:id:Cra2yPierr0t:20200730171513p:plain
図2 データフォーマット
パケットの送信に最低限必要なレジスタは以下です

f:id:Cra2yPierr0t:20200802001653p:plain
図3 送信に最低限必要なレジスタ

つまりEWRPTを駆使しながらバッファに送信したいデータを書き込み、ETXSTとETXNDにデータの先頭と末尾のアドレスを格納し、ECON1.TXRTSにビットを立てる。を行えばデータが送信されるというわけです。
ちなみに送信したいデータというのは送信先MACアドレスやタイプも含むのでEthernetⅡやIEEE802.3のフレームの仕様書とにらめっこして頑張りましょう。CRCはENC28J80が計算してくれます。

これを理解したらステートマシンを書いて制御機構を実装するだけですね、簡単!コードはここに置いておきます。

ENC28J60/enc_driver.sv at master · Cra2yPierr0t/ENC28J60 · GitHub

既にお手軽とは?みたいな雰囲気が漂ってますが僕もそう思います。

ENC28J60プログラミング

不穏な見出しですがそれはさておき、バッファメモリに書き込むデータを検討しましょう。

Ethernet Ⅱフレームの仕様は次のようになっています。

f:id:Cra2yPierr0t:20200802025345p:plain
図4 EthernetⅡフレームの仕様

プリアンブルとFCSはENC28J60がよしなにやってくれます、ありがとうENC28J60。よって我々が決める必要があるのはMACアドレスプロトコルタイプ、パケットですね。所詮まともな処理を受けることのないパケットです、適当に決めましょう。

f:id:Cra2yPierr0t:20200802030927p:plain
図5 適当に決めたフレーム

先頭にある「制御」はパケットの制御バイトと呼びENC28J60でパケットを送信するときに、フレームの先頭に付ける必要のあるものです。実際に送信はされません。今回は0Fhを付けます。プロトコルタイプの86DDhはIPv6を示しています。 またパケットの内容ですが、私はレイヤ3以上の知識を持たないので、愛すべき弊学の略称でも詰めときましょう。

さて、必要な情報を全て揃えた今、ENC28J60に書き込むデータとアドレスの組を用意し、書き込むロジックを用意する必要があります。マジ?

QuartusでROMを生成して、ROM内に必要なデータとアドレスの組を置き、上から順にENC28J60に書き込むロジックを組めばいいですね。それで出来るんですが、その「必要なデータとアドレスの組」は誰が用意するんでしょうか、当然僕です。僕しかいないので。ツールを作りましょう。

f:id:Cra2yPierr0t:20200803005801p:plain
図6 変換
出来ました。上の図のように、命令列を.mifファイルの形式に変換してくれます。オペコードのWCRやオペランドのEWRPTL等は図2と図3を参照してください。コードは以下に置いておきます。

ENC28J60/assembler.c at master · Cra2yPierr0t/ENC28J60 · GitHub

また実際にROMに書き込む命令列を以下に置いておきます。

ENC28J60/prog.S at master · Cra2yPierr0t/ENC28J60 · GitHub

実践

Quartusでプロジェクトを作成しFPGAに書き込みます。EthernetケーブルをPCと接続してWiresharkで見た結果がこちらになります。

うわっ f:id:Cra2yPierr0t:20200805075326p:plainf:id:Cra2yPierr0t:20200805075329p:plainf:id:Cra2yPierr0t:20200805075340p:plain

うっわ f:id:Cra2yPierr0t:20200805075344p:plain

出てますね。

ここまでやっておいてアレなんですが、FPGAでENC28J60を動かす利点とかは特に見いだせませんでした。一応8Mbpsは出てるっぽいのでオーバヘッドは小さい方なのかな?マイコンで動かした経験が無いのでわかませんが。

ありがとうENC28J60。そしてさようなら。

参考文献

高速Ethernet × FPGA  (FPGAマガジン No.3)

高速Ethernet × FPGA (FPGAマガジン No.3)

  • 発売日: 2014/04/12
  • メディア: 単行本

www.microchip.co.jp