💣

RP2040-E5の翻訳および解説

2022/02/26に公開

RP2040には「GPIO15が機能しない」という噂がありますがそれは正確ではありません。この噂の出どころを辿るとRP2040-E5と言われるErrattaに由来しています。本記事ではRP2040のデータシートよりこのRP2040-E5の内容を翻訳し解説します。

RP2040-E5の翻訳

概要

RP2040-E5は「RP2040が繋がっているUSBバスがビジーの場合にUSBデバイスとしてRESETステートを抜け出せない」という問題です。以下ではこの問題がどういうものか詳しく説明します。

詳細

USBバスのRESET状態はホストがSE0を10ミリ秒の間デバイスに送ることでトリガーされます。(RP2040に搭載されている)USBデバイスコントローラーはバスのリセットの後にCONNECTED状態に移行する前にアイドル状態(J-state)を800μ秒間維持することを要求します。このアイドル時間がないとUSBデバイスはバスに接続されませんし、ホストからあらゆるパケットを受け取れませんし、もちろんホスト側にデバイスとして列挙されることもありません。

このリセットはUSBデバイスが接続された時に起こります。しかし接続されたのと同じUSBハブに通信中のデバイスがある場合、USBホストはリセットしたデバイスと通信するのを遅延します。

USB 2.0及びUSB 3.0のハブは、1つの高速通信バス上に低速通信(ロースピードモード)と高速通信(フルスピードモード)を同居させるために、トランザクション翻訳機を1個以上搭載しています。USBハブの作りにもよりますが通常は1つのトランザクション翻訳機を複数のポートで共用します。

RP2040のUSBデバイスはフルスピードモードなので、USBハブに接続した際のトラフィックはこのトランザクション翻訳機を経由してやってきます。これはつまりRP2040のデバイスを繋いだポートの隣に別のデバイスが繋がれていた場合、USBホストからその別デバイスへのいくつかのメッセージをRP2040が受信する可能性があるということです。その別デバイスがさほどアクティブではない場合、例えば8ミリ秒間隔でポーリングされるマウスとか、これは問題にはなりません。しかしUSBシリアルポートのような30-50μ秒毎にポーリングされるようなものの場合、バスはとてもアクティブとなりRP2040のUSBデバイスは(800マイクロ秒のアイドル状態を観測できないため)RESET状態を抜け出せず、USBホストからは認識されません。

RP2040B2(B2改訂版)ではハードウェア的に修正されており、RESET状態のあとの800マイクロ秒のアイドル時間は必要なくなっています。

またこの問題にはソフトウェア的な回避方法があります(詳細は後述します)。またエンドユーザーはUSBシリアルポートを閉じたりそのようなデバイスを一時的に外して、RP2040のデバイスを接続した後にそれらの接続を回復することで回避できます。

ポート数の多いハブでは、RP2040のデバイスをそのようなデバイスから遠いポート(別のトランザクション翻訳機のポート)に繋ぎなおすことでも回避できるかもしれません。例として、RP2040を7ポートあるうちの1番ポートに繋ぎ、USBシリアルコンソールを7番ポートに繋ぐとこの問題は解決される可能性があります。またRP2040をビジーなデバイスが繋がっていない別のUSBハブに繋ぎ直すことも解決策となりえます。

ソフトウェア的な回避方法

デバイスがRESET状態からCONNECTED状態に遷移する際に、USBバスがアイドルになるのを800マイクロ秒の間、USBデバイスコントローラーが待機するような修正ソフトウェアを使います。この修正はGPIO15に接続されたデバッグ用の内部ロジックを最大800マイクロ秒使用します。USBデバイスコントローラーにUSBバスがJ-stateであることを信じさせるため、コントローラーにDP(D+信号線)を論理的に1に(またD-信号線を0に)であることを見せます。この修正をするためにGPIO15に物理的に何かを接続する必要はありません。(RP2040が持つUSBの)入力上書き機能を用いてソフトウェア的にこの接続を確立できます。詳しいやり方は修正パッチ:rp2040_usb_device_enumeration.cを参照してください。

補足

改訂前の古いチップ、RP2040B0とB1がこの問題の影響を受けます。改訂後のRP2040B2では修正されています。

B0とB1にはソフトウェア的な回避方法が適用できます。この回避方法はブートROMが提供するUSBメモリには適用されません。ソフトウェア的な回避方法ではUSBバスがリセットされる間はGPIO15を占有します。

RP2040-E5の解説

RP2040のUSBデバイスコントローラーにバグがあるために、RP2040をUSBハブに接続した際にその近くのポートに高速に通信し続けるデバイス(例:シリアルコンソール)がある場合にRP2040が認識されない、というのがRP2040-E5という不具合の内容です。

私がこの問題を体験した具体的な例を紹介しましょう。RaspberryPi Picoを用いた自作キーボードのファームウェアを開発する際に、1つのRaspberryPi PicoとPCを2本のUSBで接続しようとしました。片方は自作キーボードなので当然USBデバイスとして、もう一方はUART0をシリアルコンソールとしてデバッグ用にという感じです。この時4ポートのUSB2.0アダプタに両方を繋いでいたのですが、シリアルポートを開いた状態だとUSBデバイス側が認識されなくなりました。RaspberyyPi Picoはファームウェアを書き込むためにUSBの再接続が必要になりますので結構クリティカルです。なお私のケースではシリアルコンソールを別のUSBハブに切り替えることで対応しました。

最新のRP2040のリビジョンではこの問題は修正されています。それ以前のリビジョンでは前述のような運用でカバーするか、ソフトウェア的に対応する必要があります。

ソフトウェア的な回避法の内容

ソフトウェア的な回避方法の内容を簡単に解説します。

RP2040にはデバッグ用の機能として、USBデバイスコントローラーに対してUSBの信号線の内容を上書き・偽装する機能があります。それを用いてUSBバスリセットが発生した直後に800マイクロ秒の間、疑似的にアイドル状態(J-state)を維持することで、USBデバイスコントローラーを正常にCONNECTED状態に遷移させるというわけです。

その実装が前述のrp2040_usb_device_enumeration.cになります。このコードではTinyUSBと連携してUSBバスリセットを受けた際に、約1ミリ秒の間、GPIO15を通じて前述のとおりUSB信号線を操作しアイドル状態をUSBデバイスコントローラーに対して偽装します。なのでこの1ミリ秒の間はGPIO15を使えない・使わないほうが良いということになり、これが噂の発端であると推測できます。

もちろんこのワークアラウンドでは、GPIO15についての各種の状態は、利用前に保存・利用後に復帰こそしていますが、通常の作りであれば非同期的に働き、つまりUSBデバイスとして以外のタスクのコードも動くため、意図しない状態でGPIO15にアクセスしてしまう可能性が高いとも言えます。そのためUSBデバイスがCONNECT状態になるまでは、GPIO15へアクセスしないということも用途によっては必要になるかもしれません。

またパッチの内容やデータシートから考えれば、改造して利用するGPIOを変えたり、マクロを定義することで同期的に実行することもできるようです。ただこれらは理論上というだけで試したわけでもないために詳細は割愛します。

まとめ

「GPIO15が使えない」との噂の発端となったと考えられるRP2040-E5の詳細を解説しました。

この問題は最新のチップ(B2以降)では解決されています。

また「同じハブにUSBシリアルコンソールのような通信頻度の高い機器を繋がない」という運用での回避方法も存在します。

ソフトウェア的な回避方法も存在します。ただしこの方法では一時的に1ミリ秒のあいだGPIO15を占有します。これが「GPIO15が使えない」との噂の発端であると考えられます。しかしまったく使えないわけではなく、正確には「ちゃんと排他しないとGPIO15が予期せぬ動作をする場合がある」ということです。

Discussion