Spartan-3A スタータキット

〜イーサネットでの送信〜


2007/08/15

あなたは 人目のお客様です.

イーサネットでの受信ができるように なったので,次は送信をやってみたい. ていうかそもそも送信のデバッグ用に受信をまずは実装したので, いよいよ送信をデバッグできるわけだ.

前回までで紹介したソース中で, 送信用のコードはすでに入っている. 問題になるのはCRCの計算方法だ. これがまちがっていると,受信した側でチップがエラーフレームとしてはじいて しまうため,wireshark でキャプチャできない. これはチップのレベルで捨てられてしまうので, ソフトウエア的にはどうしようもない.

で,CRCの計算方法なのだけど,いろいろ調べたのだけど, まずは基礎知識として ここ に書いてある内容がとてもためになる. まずは一読するといいと思う.

で,まずは前回に受信したフレームで FCSも 0x168d631e という値として受信できているので, CRC計算をするサンプルプログラムをCで書いて計算方法の妥当性 (ていうか,どーいうふうに計算したら CRC が 0x168d631e になるのか) を見てみることにした...のだが,どうも計算結果が一致しない. 計算方法の細かいところとかをいろいろ変化させて試行錯誤したのだが,ダメ.

どうも上記ページはCRC計算の一般的な話なので,ethernet のFCSとなると, 細かい違いがあってそれだけではちょっと足りないようだ. で,いろいろ調べたのだけど,どうやらCRC計算というのはなんだかはっきりしてない 部分が多いというか,ethernet 特有の話はなかなか見つからない.

で,いろいろ探したのだけど,まずは イーサネットでの受信で紹介した Interface 2005/08 号の記事には,肝心のCRC計算の説明が無い. というのはこの記事は実は Interface 2004/04 号の記事の続編なので, CRC計算についてはそっちを見なければならない.

で,Interface のバックナンバーを調べて 2004/04 号を見たところ (こーいうときのために,ウチはCD-ROM版をそろえてあるのです. InterfaceとDesignWaveのCD-ROM版すごくべんり.おすすめ), 「10Base-T対応 LANカードの設計/製作」というそのまんまの記事があり, その中でCRC計算についてもHDL付き(VHDLだけど)で説明がある. これを見る限り,以下の点に注意しなければならないようだ.

で,上記に注意してサンプルプログラムをちょこちょこいじっていたら, よーやく計算結果が一致した.

まず,以下がサンプルプログラム.ちなみにFreeBSD用だけど, まあどこでも動くだろう.

calcrc.c

FreeBSDで以下のようにして,コンパイルして実行形式を作成.

hiroaki@teapot:~>% gcc calcrc.c -o calcrc -Wall
hiroaki@teapot:~>%

で,以下が前回に受信したフレーム (の,FCSを除いたもの).

ffffffff ffff0001 23456789 08060001
08000604 00010001 23456789 c0a80a01
00000000 0000c0a8 0a020000 00000000
00000000 00000000 00000000
上記フレームの内容をそのままarp.txt というファイルにして,以下のようにして実行する.
hiroaki@teapot:~>% cat arp.pkt | ./calcrc
crc0 = 68b1c678
crc1 = 168d631e
hiroaki@teapot:~>%
crc1が 0x168d631e となっていて,CRCが計算できていることがわかる.

で,サンプルプログラムの計算方法を参考にして,ether_tx.v を修正した. 以下,修正済みのファイルを一通り.

main.v
rx.v
tx.v
ether_ctrl.v
ether_rx.v
ether_tx.v
memory.v
s3astarter.ucf

ちなみにCRCの計算に関しては,わかりやすくするために専用のステートを設けてあり, フレームの送信の前にまずCRC計算が行われる. これは送信が遅れることになるので非常にムダで改良の余地がありありなのだけど (本来は送信しながら計算すべき), まあ実験用なのでわかりやすさと手直しのしやすさを優先する.

では,送信のテストをしてみよう. まずはいつもどおり,フレーム送信に使う FreeBSD マシンの設定. (「いつもどおり」「前回と同様」とか書いてある部分でわからない点があれば, ここからイーサネット関連の日記を最初から 参照してほしい.きっと以前のどこかで説明してあるので)

teapot# ifconfig udav0 up
teapot# ifconfig udav0 media 100baseTX
teapot# ifconfig udav0 ether 00:01:23:45:67:89
teapot# ifconfig udav0 
udav0: flags=108943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500
        ether 00:01:23:45:67:89
        media: Ethernet 100baseTX
        status: active
teapot#
今回はFreeBSDでは受信だけできればいいので,IPアドレスは設定する必要は無い. MACアドレスは前回と同様,実験用として 00:01:23:45:67:89 というものすごーく てきとうなものに設定している.

次にスタータキットをシリアルで FreeBSD マシンに接続し,スタータキットを起動.

teapot# cu -l /dev/cuad0
Connected
READY
READYが出たらSコマンドでリセットし,Gコマンドでリンクアップの確認. これもいつもどおり.
teapot# cu -l /dev/cuad0
Connected
READY$S+SET$G+7809$G+780d
FreeBSDマシンで wireshark を起動して,キャプチャの準備をする. FreeBSD-6.xでのキャプチャ時の注意については 前回を参照.


キャプチャを開始した状態

で,以下の内容のフレームを送信してみる.

00 01 23 45 67 89 11 22 33 44 55 66 aa bb cc dd ee ff 00 11 ...
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~ ...
宛先MACアドレス   送信元MACアドレス 以降はてきとうなデータ
先頭6バイトが宛先MACアドレス, 次の6バイトが送信元MACアドレス, 以降はてきとうなデータとなる.

宛先MACアドレスは,FreeBSDマシンに ifconfig で設定した実験用の MACアドレスにしてある. まあ実は wireshark ではプロミスキャスモードでキャプチャされるので, どんなMACアドレスでも大丈夫だとはおもうがいちおう合わせておいた.

Lコマンドにより,フレームサイズを64バイトに設定する. ちなみにフレームの送信用としては,Lコマンド,Dコマンド,Tコマンドというのが 作成してある. これらに関しては前々回で 説明してあるのでそちらを参照.

teapot# cu -l /dev/cuad0
Connected
READY$S+SET$G+7809$G+780d$L00000010+TX
                         ~~~~~~~~~~
                         Lコマンド
で,Dコマンドを用いて,上記のデータをスタータキットに設定する. 64バイト全体に対してデータ設定するのは面倒なので,途中までしか設定しない.
teapot# cu -l /dev/cuad0
Connected
READY$S+SET$G+7809$G+780d$L00000010+TX$D00012345+TX$D67891122+TX$D33445566+TX$Daabbccdd+TX$Deeff0011+TX
                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                      Dコマンド

Tコマンドで実際に送信する.

teapot# cu -l /dev/cuad0
Connected
READY$S+SET$G+7809$G+780d$L00000010+TX$D00012345+TX$D67891122+TX$D33445566+TX$Daabbccdd+TX$Deeff0011+TX$T+TX
                                                                                                       ~~~
                                                                                                       Tコマンド


実験用フレームのキャプチャ結果

おー,wiresharkで実験用フレームが受信できている.フレームの内容もバッチリだ. データを設定しなかった後半部分は,オールゼロになっている.

送信データのサイズとして64を設定した (Lコマンドで0x00000010(10進数だと16)を設定した.Lコマンドは4バイト単位で指定するので, 16×4=64バイトとなる)ので,64バイトが受信できている. 実際にはFCSがついて68バイトが送信されていることになる. 受信側ではFCSのチェックの後,FCSを取り除いた64バイトをカーネルに渡して いるわけだ.

さて,ここまでで気がついたことなのだけど, ethernet のフレームは基本的にリトルエンディアンだ. これは,メモリの下位アドレスからデータをシフトレジスタを通して ビット列として(CRCの計算などをしながら)順に送信することを考えると, 非常にまっとうな設計だといえる (単に下位側に順次シフトして,最下位ビットから送出するだけ). しかし,たとえば今回の

00 01 23 45 ...
というフレームは,2進数にすると
00000000 00000001 00100011 01000101 ...
~~~~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~~~~
  0x00     0x01     0x23     0x45
となる.これはリトルエンディアンで送信されるので, 実際に送信されるビット列は
00000000 10000000 11000100 10100010 ...
←こちらから送信される
という順番になる.つまり送信される順番は, 16進での表記を8bitごとに反転させた形になる. いや,言いかたを変えると,16進表記では, 実際に送信されるビット列を8bitごとに反転させた形になっている. 16進表記では1バイト(8bit)ごとに書かれるから, このようなことになるわけだ.これは受信データを表記する場合も同じだ.

上のほうで紹介したCRC計算のサンプルプログラムは,このためテキストで読み込んだ データを8bitごとに反転させて計算 (正確には,8bit(1バイト)ぶんずつデータを読み込んで, リトルエンディアンで計算している)し, 再び8bitごとにビットの左右を反転してから表示している.

このように,16進数での表記と実際に扱うデータはひっくり返さなければならない ことが多々あるので,データの入力時や値の表示時には注意が必要. とくに main.v では,データの入力や表示など,いろんなところでこの 「ひっくり返し」をしているので参考にしてほしい.


メールは kozos(アットマーク)kozos.jp まで