もう少しこのフロー制御を判りやすい形で示してみよう。例えば受信側の処理がやや遅く、転送頻度の倍の処理時間が掛かるとする。ここで送信側から8パケットを送るケースを考えてみる(図1)。

1番目から4番目までのパケットは、受信側のバッファが空いているからこれは問題なく転送が出来る。ただし受信側の処理が滞っているので、4番目を転送し終わったあとで、やっと最初の2パケットの処理が終わり、バッファが空いている状態だ。ここで5・6番目の転送が行われ、この時点で一度受信側のバッファが一杯になるが、その直後に7番目の転送が行われ、受信側のバッファは再び一杯になる。

こうなった場合、送信側は8番目のパケットをしばらく転送できないまま待機することになる。転送が再開できるのは、受信側からLCRD_Dが届き、4番目のバッファが空いたことが確認できてからとなる。今回の場合はこれで転送が終わるが、例えば転送すべきパケットがもっと多い場合、以後はLCRD_x(x:A~D)が受信側から来るたびに1パケットづつ転送が行われるという形になる。要するに受信側の転送準備が整うまで、送信側は転送を行わないわけで、無駄の無いフロー制御が行われている事がお判りいただけよう。

では、次にリトライが発生する場合を考えてみよう。初期状態(Photo01)から、送信側Header Bufferに最初のHeaderを設定され(Photo02)、送出される(Photo03)。ここまでの手順は全く一緒だ。問題は、ここで受信側がCRCを計算して異常を検出した場合だ(Photo04)。この場合、受信側は送信側にL_BADを送り返す(Photo05)。これを受けて、送信側は一度タイマーをリセットした上で、再送宣言を行い(Photo06)、同じHeader Bufferの中身を再送することになる(Photo07)。CRCの異常が一時的な問題で、再送されたものが正常に伝達された場合(Photo08)は、以後は正常転送時と同じようにLGOOD_xを送信し(Photo09)、受信側でBufferの内容を処理し(Photo10)、最後にLCRD_xを送信して(Photo11)終了という運びになる。

Photo01: 双方のバッファは空で、ポインタはBuffer Aを指し、またCreditは4となっているという初期段階。

Photo02: 勿論Tx側のSequence Numberは1になる。

Photo03: 送信側Creditの残量が3に減り、受信側Header Bufferにデータが入る。

Photo04: CRC異常なので、Sequence NumberやBuffer Creditはまだ変更されない。

Photo05: 再送が入ると、CREDITHPTIMERまでリセットされるのがちょっと興味深い。

Photo06: LRTYを受信したら、受信側は先の転送と同じバッファにもう一度書き込む処理を行うし、Sequence NumnerやHeader Buffer Credit、LCRD_x indexなどは変更しない。

Photo07: Link Layerではあくまで「転送中に何らかの理由でエラーが発生した」事のみをハンドリングするので、内容のデータが根本的に間違っていた的なエラーは勿論未対応。そうした問題は上位層でカバーすることになる。

Photo08: ここから先は、最初の転送でCRCが正常だった場合と同じになる。

Photo09: LGOOD_xが送られた時点で対象のHeader Bufferの内容は正常転送されたと判断されるから、送信側のBufferをクリアできることになる。

Photo10: この再送シーケンスに関しては上位層からは透過(下位層で勝手に処理される)ので、特に再送が入ったといったレポートは上がらない。

Photo11: この後処理も正常転送時と同じ。

ちなみに仕様上、受信側からLBADが返されてきたときに、送信側でそれを無視するという選択肢はない。送信側はこの場合、必ずLRTYをまず送信してから、再送することが義務付けられている。もっとも、一時的なエラー(例えばたまたまバーストノイズが載っかったとか)であれば再送で解決する場合もあるが、原因がもう少し複雑で、エラー状態が暫く続くようなことがある。こうした場合、タイムアウトするまで延々とリトライするのは無駄でしかない。そこでエラーに関してはこんな具合に処理が定められている(Photo12)。

Photo12: 例えばCRCエラーならば、2回までは再送だが3回発生したら転送を中断してRecoveryに遷移する。

Link層における処理に関しては、以上で大体説明が終わった形になる。PCI Expressの場合だと、このLink層のStatus比較的細かくソフトウェアから取得することが可能だが、USB 3.0ではこれも若干省略されている。例えばPIPE I/Fを使う場合、Control Signal Decode tableと呼ばれるレジスタの値により8種類の状態が識別できるが、USB 3.0ではこれが6種類に簡潔化されているなど。

ただUSB 3.0の場合、仮に細かいエラーをソフトウェアなどに通告しても出来ることは余りないし、エラーを通告してハンドリングさせるのには余分なコストが掛かるから、それよりは成功か失敗程度のステータスが伝わればいいと割り切ったのであろうと思われる。PCI Expressと違い、USB 3.0は(今はまだPC以上のプラットフォームのみがターゲットの)複雑なI/Fだが、中長期的にはUSB 1.1/2.0を置き換えるような民生機器向けの安価なI/Fになることを狙っている訳で、そうした安価な民生機器に複雑なドライバを書かせるよりも、多少エラーリカバリに無駄が多くても簡単なソフトウェアで記述できるシステムの方が望ましいと判断しているのだろう。

ということで、次からはProtocol Layerの話である。

(続く)