しばらくメモリと格闘してたけど うまく動かないので,ちょっとイーサネットをいじってみようと思った.
イーサネットなのだけど,スタータキットでは Spartan3A と PHY とが MIIインタフェースというので繋がっているらしい. MIIというのはMACとPHYの間でやりとりするための標準的インタフェースらしい.
MIIインタフェースやそのへんに関しては,Interface 2005/08 号の 「Ethernetにおける内部インターフェース − MIIの動作」 というそのまんまの記事があって非常に参考になる.まあ結論からいうと, その記事のとおりにやってとりあえずリンクアップするところまではうまく動いた. (ただしnRSTに注意)
さらに,Spartan-3A スタータキットのユーザーガイドによれば, スタータキットに乗っているPHYチップは SMSCのLAN8700というものらしい. これについては Spartan-3A スタータキットのユーザーガイドの イーサネットの説明の,Related Resources のところで紹介されている SMSCのページ からデータシートがダウンロードできる.
作成したのはこんな感じ.
main.v
ether.v
tx.v
rx.v
s3astarter.ucf
いつものように,シリアル経由でコマンドを入力するとそれに応じて動作するように してある. tx.v, rx.v はシリアル送受信用のモジュールで,相変わらず以前からの流用. ただ,シリアルからの入力をエコーバックするように改良してある (エコーバックは実際には main.v の内部で行っている).
とりあえずリセットと制御レジスタの設定,ステータスの取得のみ実装してある. 具体的にはMDCとMDIOによるレジスタのリード・ライトのみ実装してある. というわけでフレームの送受信はまだできない.
シリアル経由では,以下のコマンドを送信することで指定された動作をする. ちなみにリセットは起動時に自動的に行われる.
注意しなければならないのは MDIO ピンで,これは入出力両用(inout)になっている. inout のピンをどう扱うのかはメモリのところ でも悩んだのだが,読み込むときはハイインピーダンスにすることでうまくいった. 具体的には main.v の内部で
wire ether_ctrl_mdio_z; wire ether_ctrl_mdio_out; assign E_MDIO = ether_ctrl_mdio_z ? 1'bZ : ether_ctrl_mdio_out;のようにして,E_MDIO は ether_ctrl_mdio_z に応じてハイインピーダンスと ether_ctrl_mdio_out による出力が切り替わるようになっている. 入力したいときは ether_ctrl_mdio_z を立ててハイインピーダンスにした状態で E_MDIO を読み,書き込みたいときは ether_ctrl_mdio_z を落としてから 書き込みたいデータを ether_ctrl_mdio_out に出力する, という書き方でとりあえずうまくいった. FPGA内部では inout という信号の考え方は無いので, main.v と ether.v の間は MDIO は入力用と出力用で, ectl_mdio_in, ectl_mdio_out という2本の信号に分けてやりとりしている. 外部的には入力と出力が共用で1本の信号でも, 内部ではこーいうふうに入力と出力で2本に分けるのが,FPGAではふつうみたい.
あと main.v はメモリのところで作成した main.v をベースにして書いているので,部分的に名残があるけどまあよしとする. そのうちきれいに書き直そう.基本的には全体としての状態遷移と,各モジュール (シリアル送信(tx.v),シリアル受信(rx.v),PHY操作(ether.v))とのやりとりをする.
あと,スタータキットには E_NRST というピンがあって, これはどうも MII で定義されているピンではないらしく(?), Interface 2005/08 号にはとくに説明は無い. スタータキットのユーザーガイドでは,Active-Low Reset という説明があるだけだ. ついでに Spartan-3E スタータキットではこのピンは存在しないらしく, そちらのユーザーガイドにはとくに説明は無い. LAN8700のデータシートを読むと,LAN8700のリセット方法には3種類あって, そのひとつとしてnRSTを利用するものがあるらしい (ちなみにその他のひとつとして,MIIインタフェースを用いるリセットで, Interface 2005/08 号でも説明されているものがあるようだ). Active-Low という説明なので,落としたときにリセットがかかるようだ. まあ名前の先頭にも n がついていることからも,落としたときにリセットが かかることが推測できる.
まあリセット操作は MII インタフェースを使った方法 (Interface 2005/08 号で説明されているもの)で行う, ていうかすでにそういうふうに書いてしまったので, nRSTを使う必要は無いのかなーとも思い, E_NRST は常に立てておくだけにしてみたのだがうまくいかない. 最初に E_NRST を一定期間(適当)落とすことでリセットをかけて, で,E_NRST を立ててから初期化処理を行うようにしたらうまく動いた. (ちなみに最初のうちは E_NRST をとくに何もしなかったらPHYがうまく動かなくて, E_NRSTを落としてから立てるようにしたらうまく動くようになった. おそらく E_NRST が落ちっぱなしだったためにリセットかかりまくって うまく動作していなかったのだろう)
で,動作確認.まずはコンパイル.で,スタータキットにダウンロード. 注意としてスタータキットへのダウンロードの前に,シリアルで接続しておくこと (起動時のREADYメッセージを確認したいので). 今回もいつも通り,FreeBSDから cu で接続する.
あと今回は,イーサネットでとりあえずリンクアップさせるために, ハブかなにかを用意する必要がある. ところが残念ながらハブに空きポートが無かったので, とりあえず FreeBSD にUSBのLANアダプタ挿して, クロスケーブルで繋いでリンクアップさせることにする. 自作のクロスケーブル使うあたりに,ちゃんと動作するか一抹の不安があるが (なにしろ自作のお粗末なイーサネットコントローラだしね), まあうまくいかなかったらそのときに考えよう.
で,FPGAにダウンロードする.
リセットがかかり,READYメッセージが出力される.
つぎにSコマンドを発行し,レジスタの設定を行う.
応答として「SET」が返ってきている.
つぎにGコマンドで,状態レジスタ(PHYレジスタ1)の値(16bit)を取得する.
値として,0x7809が返ってきている.
状態レジスタに関してなのだが, Interface 2005/08 号によれば以下のようになっている.
ビット | 意味 |
15 | 100baseT4が利用可能時なら1 |
14 | 100baseTX全二重が利用可能時なら1 |
13 | 100baseTX半二重が利用可能時なら1 |
12 | 10baseT 全二重が利用可能時なら1 |
11 | 10baseT 半二重が利用可能時なら1 |
10〜6 | 予約 |
5 | オートネゴが完了したら1 |
4 | リモートフォルト検出で1 |
3 | オートネゴ利用可能なら1 |
2 | リンクアップで1 |
1 | Jabber検出で1 |
0 | 拡張レジスタあるならば1 |
ここで重要なのはビット15〜11から得られる対応モードと,ビット2から得られる リンク状態である. 値としては0x7809が返ってきているので,以下のようになっている.
usb_load="YES" if_udav_load="YES"で,udav のモジュールが有効化されれば,あとはおもむろにUSBポートに挿して, ifconfig で状態を見ることができる.
teapot# ifconfig plip0: flags=108810<POINTOPOINT,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 aue0: flags=108843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 inet X.X.X.X netmask 0xffffff00 broadcast X.X.X.255 ether 00:XX:XX:XX:XX:XX media: Ethernet autoselect (100baseTX <full-duplex>) status: active lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 inet6 ::1 prefixlen 128 inet 127.0.0.1 netmask 0xff000000 udav0: flags=108802<BROADCAST,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 ether 00:XX:XX:XX:XX:XX media: Ethernet autoselect (none) teapot#udav0 が見えている.
挿しただけだとインタフェースがダウン状態なので,ifconfig で活性化する.
teapot# ifconfig udav0 up teapot# ifconfig udav0 udav0: flags=108843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 ether 00:XX:XX:XX:XX:XX media: Ethernet autoselect (none) status: no carrier teapot#UPフラグが立っている.
さらに,メディアタイプを100baseTXに固定する. スタータキット側は固定だが,USB LANアダプタ側はオートネゴで 自動的に設定されるような気もするが,まあいちおう固定で設定.
teapot# ifconfig udav0 media 100baseTX teapot# ifconfig udav0 udav0: flags=108843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 ether 00:XX:XX:XX:XX:XX media: Ethernet 100baseTX status: no carriermedia が 100baseTX に設定されていることに注目. リンクアップしていないので,status は no carrier のまま.
この状態でクロスケーブルをスタータキットに接続する.
緑ランプ(リンクランプだと思う,多分)が点灯している. リンクアップしているのかしら.
Gコマンドで,状態レジスタの値を見てみる.
今度は値として0x780dが返ってきている.さっきは0x7809だったので, 状態レジスタのビット2が立ち,リンクアップしたことがわかる.
さらに,USB LAN アダプタのリンクランプも点灯している. さらにさらに,ifconfig を見ると
teapot# ifconfig udav0 udav0: flags=108843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500 ether 00:XX:XX:XX:XX:XX media: Ethernet 100baseTX status: activeとなっていて,status が active になっている. 問題無くリンクアップしているようだ.
以下はケーブルの抜き差しをしながらGコマンド発行したところ.
0x780dと0x7809が交互に出ている.リンクアップ/ダウンを検出できている.
nRSTの扱いでちょっとハマったが,まあとりあえずうまくいった. メモリがウンともスンとも動いてないこと に比べれば雲泥の差.やっぱし動いているのが目で見えるのはやりやすいし楽しいね.
次はいよいよフレームの送受信を実装してみたい.