« GOWINのGW1NR_9をバウンダリスキャンすることができた | トップページ | コロナ騒ぎと新しい世界 »

2020.03.10

MPSSE-JTAGを理解する

FTDI社のFT2232シリーズにはMPSSEという、汎用のUSBマルチプロトコルシリアル変換機能が搭載されています。MPSSEはもともとJTAG専用というわけではなく、SPI、I2Cなどで汎用のシリアル変換チップを作るためのもののようです。

今日はそのMPSSEの機能について調べてみました。

 

まずMPSSEを使うことができるデバイスは、FT2232D、FT2232H、FT232H、FT4232Hです。この中でFT2232D以外はHighSpeed対応です。

ここではFT2232Hを例に説明していきます。

FT2232Hを使う場合、TCK=bit0、TDI=bit1、TMS=bit2、TDO=bit3になります。FT2232HにはChannelAとChannelBがありますが、どちらを使うこともできます。ADBUSのbit4~bit7、ACBUSに属するbit8~bit15はGPIOにすることも可能です。TCKクロックの速度は60MHz÷分周比となりますが、分周比は2,4,6,8・・なので、30MHz、15MHz、10MHz、7.5MHz・・・への設定が可能です。

読むべきアプリケーションノートはAN108とAN129です。

https://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf

https://www.ftdichip.com/Support/Documents/AppNotes/AN_129_FTDI_Hi_Speed_USB_To_JTAG_Example.pdf

AN129にはMPSSEを使うJTAGのサンプルプログラムが載っていて、AN108にはMPSSEのコマンドの解説が載っています。

MPSSEは、OUT FIFOに入っているコマンドを順番に実行し、結果をIN FIFOに入れていきます。そのコマンドは<コマンドコード>[<長さ>][<データ>]の形式で、FT2232Hはそれを解釈して実行していきます。

そのコマンドの説明がAN108に書かれています。

AN129には実際の手順が書かれています。まず以下のようにしてFT2232Hを初期化してMPSSEモードに入れます。

FT_Open(0, &ftHandle); // 0は最初に見つかったデバイス。FT2232HのChannelAが使われる。
FT_ResetDevice(ftHandle);
// 受信バッファのサイズを取得し残っている残っているデータを受信
FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead);// 受信バッファを最大値に
FT_SetUSBParameters(ftHandle, 65536, 65535);// イベントが発生しないようにする
FT_SetChars(ftHandle, false, 0, false, 0);
FT_SetTimeouts(ftHandle, 0, 5000);
FT_SetLatencyTimer(ftHandle, 16);
FT_SetBitMode(ftHandle, 0x0, 0x00);
FT_SetBitMode(ftHandle, 0x0, 0x02); // ここでMPSSEモードに切り替わる
Sleep(50);

ここまでがFT2232HのUSB機能自体の初期化です。

そうしたら次にMPSSEコマンドを与えて、JTAG機能の初期化を行うのですが、最初に0xAAという未定義のコマンドを与えてわざとエラーを起こさせます。するとFA AAというレスポンスが返るので、これを利用して受信バッファの同期を取ります。

次に、

  • 0x8A:60MHzクロックを使う。FT2232Dは12MHzなので
  • 0x97:アダプティブクロックを無効にする。ARMのデバッグはRTCKを使うが、ここでは使わない
  • 0x8D:3phaseクロックを無効にする
  • 0x80 0x08 0x0b:LowByteを TMS=1 TDO=IN TDI=0 TCK=0にする
  • 0x82 0x00 0x00:HighByteをINにする
  • 0x86 分周比 分周比:クロック速度の設定
  • 0x85:ループバックをディゼーブルにする

と、非常に様々なオプションを初期設定します。

いよいよJTAGのTDIやTMSにパターンを出力していくわけなのですが、MPSSEにはTMSの与え方だけで6種類、TDIの与え方で12種類ものコマンドがあります。クロックの立下り or 立ち上がりでデータを有効にするとか、データを入力するかとか、長さをByteで与えるかbitにするかとかがあります。

原理的には0x00~0x7fまでの128種類のコマンドがあるのですが、AN_129で実際に使われているのは

  • 0x4B・・TMS操作。TDOは入力しない。
  • 0x3B・・TDI/TDO入出力。長さはbit単位。クロックの立下りでTDI出力し、クロックの立ち上がりでTDO入力。
  • 0x6B・・TMS操作。TDOは入力する。
  • 0x8F・・データなしでひたすらクロックを与える

の4種類のみでした。

このほか、実際のJTAGツールを作るには

  • 0x39・・TDI/TDO入出力。長さはbyte単位。クロックの立下りでTDI出力し、クロックの立ち上がりでTDO入力。

も必要でしょう。0x3Bコマンドは8bitまでのシフトしかできないのでいささか効率が悪いようです。

JTAGの操作では、Shift_DRやShift_IRの最後でTMSを上げるという操作が必要になるのですが、MPSSEにはそういうコマンドはありません。そのため8bitのレジスタシフトであれば、7bitの0x3Bコマンドと1bitの0x6Bコマンドに分けなければなりません。そこが面倒なとろです。

0x4Bコマンドと0x6Bコマンドでは、bit7がTDIから出力され、bit[6:0]がTMSから出力されるので、データシフトの最後の1bitは0x6Bコマンドを使って出すというわけです。

これらのコマンドを駆使してデータ出力するのですが、例えば35bitのデータシフトをしたいのであれば、「0x39コマンドで4バイトを出し、0x3bコマンドで2bitを出し、0x6bコマンドで1bitを出しながらTMSを上げる」というふうに分解する必要が出てきます。面倒ですね。

  

そのほかいろいろ分かったことは、

  • DigilentのUSB-JTAGは、MPSSEでもアクセスできる
  • MPSSEコマンドとMPSSEコマンドの間は200nsくらい空く
  • 0x3bコマンドは8bitまでしか送れない
  • 0x39コマンドでバイト単位で送っても8bitごとに50nsくらい空く
  • 3Bコマンド(TDO受信あり)も1Bコマンド(TDO受信なし)も速度は変わらない
  • 受信したデータはMSBから詰まっていく。そのデータはコマンドごとにクリアされないので、TMSのときの7bit+1bitのように最後のデータを受信する場合には自然につながる(最後から2番目のバイトは捨てる)

これでMPSSEを使ったJTAGケーブルが作れそうですね。

ただ、これだけでは力不足です。XILINXのXVCプロトコルはTMSやTDIといった役割を無視した単純なGPIOを期待したものなので、TMSやTDIをそのまま出せるようでなければなりません。0x80によるポート出力と0x81によるポート入力で単純にカチカチやってみると、TCK=1.6MHzくらいとなりました。

遅いけれどもXVC対応のケーブルも作れるでしょう。

 

|

« GOWINのGW1NR_9をバウンダリスキャンすることができた | トップページ | コロナ騒ぎと新しい世界 »

コメント

コメントを書く



(ウェブ上には掲載しません)




« GOWINのGW1NR_9をバウンダリスキャンすることができた | トップページ | コロナ騒ぎと新しい世界 »