2011年05月05日

FPGAライタの設計

AVR互換コアも一段落。つぎは、これを JTAG で書きこむものをでっち上げる。JTAG インターフェイス は独自使用の serjtag 。FT-232R も考慮する。

まずは、大昔の記事『USB910のJTAG拡張案』をひっぱりだして、どのように serjtag を設計したのか思い出す。

    serjtag は Digilent の USB JTAG Cable のプロトコルを シリアルで使えるように変更したもの。概念としては互換になっている。

    そのプロトコルは、どういうものだったかというと...

    • コマンド NULL

      受信パケット要求のコマンド。このコマンドを発行したときだけ、受信パケットが送られてくる。

    • コマンド ENABLE

      初期化と終了コマンド

    • コマンド SET

      TDI/TMS//TCK の値設定。

    • コマンド PUT_TDI_BITS

      TDI にビット列を送り込む。

      オプションには、受信データを記録する RECIEVE と TMS の状態をセットする TMS_HIGH がある。

      受信データを受け取るには、RECIEVE を使い記録して、コマンド NULL で要求する。

    • コマンド PUT_TMS_TDI_BITS

      TDI/TMS の組の データ列を送り込む。

      オプションには、受信データを記録する RECIEVE がある。

    • コマンド GET_TDO_BITS

      TDI/TMS の状態を設定して TDO を読み取る。

      コマンド NULL で要求してはじめて、受信できる。

    6 種類しかないが、JTAG の操作には十分。

    この 6 種類をメソッド化して、CABLE ドライバを定義する。上位レイヤでは、ビットストリームのデータを元に、この 6 種類を使って操作するように設計すれば良い。

さて、ビットストリームをどのように扱えば良いのだろう? 観点は 2 つある。ひとつは、JTAG の操作。もうひとつは、ファイルの形式。

    まずは、
  • xulaload
    をみてみよう。

    xula.py をみてみると、JTAG を操作しているように見える。

    TMS_TDI_CMD = 0x42 # Send a single TMS and TDI bit.
    TMS_TDI_TDO_CMD = 0x43 # Send a single TMS and TDI bit and receive TDO b
    it.
    TDI_TDO_CMD = 0x44 # Send multiple TDI bits and receive multiple TDO
    bits.
    TDO_CMD = 0x45 # Receive multiple TDO bits.
    TDI_CMD = 0x46 # Send multiple TDI bits.

    Xula のコントローラには、こんなコマンドがあるらしい。上記 6 種のコマンドで 組み立てることは可能なように思える。Python をそのまま使うのであれば、移植は難しくなさそう。

    だが、基本ツールでは、Python だけでなく perl も使いたくない。そして使うだけでなく、ちゃんと理解したい。

      基本ツールで使うスクリプトは、sh , awk, sed ぐらいにすることにしている。ライブラリを使うなら基本 C 。対応するのが serjtag のみなら スクリプトで書けそうだが、FT-232R が想定に入っているから C にすることになる。

  • XAPP139 : [PDF] バウンダリスキャン (JTAG) 使用による Virtex FPGA のコンフィギュレーション (日本語版)
  • XAPP452 : [PDF] XAPP452 Spartan-3 FPGA Family Advanced Configuration

    xula.py に XAPP139 と XAPP452 の文字が見える。これを読めば理解できるだけの情報が得られるのだろう。

XAPP139 を見てみる。

    このドキュメントに コンフィグの基本が書いてあるようだ。

    まずは、シーケンス。

       :
      CFG_IN をロード
      ビットストリームをロード
      JSTART をロード

    どうも、このあたりが肝心な部分らしい。

    self.LoadBSIRthenBSDR(self.CFG_IN, bs)
    self.tlr()
    self.LoadBSIRthenBSDR(self.JSTART, None)
    return

    xulaload xula.py は 1:1 に対応するようなコードになっている。tlr() というのは、"Go to TEST-LOGIC-RESET" だそうだ。ビットロードのデータは、bs で

    BitFile(bitfilename)

    で生成している。
    一方、実際に bs を送るのは、sendbs() で

    Send bitstream over TDI, raising TMS for last bit

    コメントにはこう書いてある。last bit には気を付けないといけないが、基本は、ビットストリームを送るだけらしい。あと、指定するファイルは xxx.bit で PROM ファイルではないようだ。

XAPP452 を見てみる。

    XAPP139 には、ビットストリーム・ファイルの説明は載っていない。それが載っているのは XAPP452 。だが、xulaload bitstream.py は、中身はほとんど見ていない。

    シンプルなのだが、何をやっているか分からないので、xilprg を見てみた。

      xilprg では、1 バイト read してみて、'a','b','c','d' なら読み飛ばす。'e' ならデータフィールドなので、読み込む。という処理をしていた。

      フィールドの読み込みは、最初に length が BIGENDIAN で入っている。
      サイズは、'a','b','c','d' なら 2 バイト。'e' なら 4 バイト。
        xilprg では、次のコメントが付いている。参考まで。

        case 'a': // NCD File Name
        case 'b': // Part Name
        case 'c': // Creation Date
        case 'd': // Creation Time

      また ファイルの先頭から 13 バイト 無条件に読み飛ばしていた。

    これぐらい知っていれば .bit ファイルは扱える。だが、xilprg は PROM ファイルの .mcs ファイルも扱えるようだ。.mcs ファイルは、ihex フォーマットらしい。.mcs ファイルを扱うようにすれば、同じファイルを SPI FLASH に書いたり、直接 CONFIG したり出来て便利なような気がする。

bit ファイルを読み込むテスト

    簡単なプログラムで確認してみた。

    今は自力で作れないので、『MZ-700をつくる』の mz700fpga_X200_051120.lzh から mcs ファイルと bit ファイルを借用した。

    で見てみると

    # 'a' len = 10
    # 'b' len = 11
    # 'c' len = 11
    # 'd' len = 9
    # 'e' len = 130952

    という風に格納されていた。


    0000: 87 ff ff ff aa 99 55 66 30 00 80 01 00 00 00 07
    0010: 30 01 60 01 00 00 00 34 30 01 20 01 40 00 3f e5
    0020: 30 01 c0 01 01 41 40 93 30 00 c0 01 00 00 00 00
    0030: 30 00 80 01 00 00 00 09 30 00 20 01 00 00 00 00
    0040: 30 00 80 01 00 00 00 01 30 00 40 00 50 00 7f 88
    0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    *
    00b0: 00 00 00 00 00 01 98 00 00 00 00 00 00 1c c0 00
    00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    *

    先頭をダンプするとこうなっていた。

    参考までにプログラム: bit_test.c

mcs ファイルを読み込むテスト

    次は、mcs ファイル。

    ihex のフォーマットは、『HEXファイルフォーマット』を参照。忘れたらいつもここを見ている。

    Xilinx にも説明がある。『PROMGen - PROM/EEPROM ファイル フォーマットの説明』を見ると、02/04 でアドレスを拡張しているようだ。

    base_addr 00000000
    base_addr 00010000
    ln 8188 size 130952

    みてみると、04 を使っていてサイズは同じ 130952 バイト。あと、アドレス 0 から連続してデータが入っている。(隙間はない)

    0000: ff ff ff ff 55 99 aa 66 0c 00 01 80 00 00 00 e0
    0010: 0c 80 06 80 00 00 00 2c 0c 80 04 80 02 00 fc a7
    0020: 0c 80 03 80 80 82 02 c9 0c 00 03 80 00 00 00 00
    0030: 0c 00 01 80 00 00 00 90 0c 00 04 80 00 00 00 00
    0040: 0c 00 01 80 00 00 00 80 0c 00 02 00 0a 00 fe 11
    0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    *
    00b0: 00 00 00 00 00 80 19 00 00 00 00 00 00 38 03 00
    00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    *

    だがダンプしてみると .. 似ているようで何かちがう。

    参考までにプログラム: mcs_test.c

これで、ファイルフォーマットは分かったし、読めるようになった。JTAG の操作は、XAPP139 と xulaload,xilprg を読めばいけそうだ。準備はできた。

... と思ったのだが、bit と mcs の違いについて 検索していると 『なひたふJTAG日記』 になにやら情報が。

  • SPIのフラッシュROM書き込みツール

    ひとつはこれ。JTAG で通信して SPI FLASH に書くことができるそうだ。ユーザロジックで JTAG と通信できることを知らなかった。

    ... というか普通にバウンダリスキャン? IOB のレジスタと通信すれば良いだけ?

    ug332 (日本語版 p263) に書いてあった。BSCAN プリミティブ(BSCAN_SPARTAN3A 等)を使えば プライベートスキャンチェーンをインプリメントできるそうだ。

    Spartan-3A 用の『ライブラリガイド(pdf)』を見ると TCK/TDI/TDO1/TDO2/TMS のインターフェイスがある。これに SPI をスレーブとしてつなげれば良いわけか。

    メモ: 2つチャネルを作れるから ISP の機能も組み込むと良いかも知れない。プログラムのデバッグで FPGA の Implement はしたくないし。

    ISP は、TPI が手本の独自仕様にしようと思う。だいたい JTAG でアクセスする手順が煩雑で互換性など取れない。AVR互換コア(rtavr)の中には作らないかも知れない。RESET 時にはバスを開放するような仕様にして、外に付ける。

    BSCAN プリミティブは、rtavr に入れたくないし、SPI も PORT 経由で(外部から)接続しないといけないのが理由。

      知っていたら、artemis ボードのデザインも随分変わったかも知れないし、rtavr も USART 付けなかったかも。現時点では、今のデザインで良いと思っているから、知らなくて良かったと言うべきか。

    この方式の利点は、(1) デバイスに対して 1つビットストリームを用意すれば良いのと、(2) JTAG ケーブルだけで良いこと。

    (1) は、定義しなければならないピンが存在しないから。使う方も作る方も面倒がなくて良い。

    (2) は、SPI - SPI コンバータで作れそうだから、これが主目的だとプロセッサが不要になってしまう。だが、通信手段のひとつと考えれば、便利そうに思える。

    ただ、クロックはどうするのだろう? 外部に影響を与えないで、クロックが作れたりするのだろうか?

    ... 違うようだ。JTAG から作り出していると書いてある。rtavr は 2X クロックが必要なうえ、SPI スレーブは 1/2 CLK でしか動かない。4倍のクロックが必要だから無理。

    遅延の設定を使ってクロックが生成できるのなら嬉しい。だいたい コンフィグでそれをやっているのだから、出来ないはずはないと思うのだが...


      ちょっと検討してみた。

      『FPGAの部屋:Spartan3Eの入力用プログラマブル遅延素子』を見ると 3E で既に プログラマブルな遅延機能が入っているそうだ。

      記述は、UCF ファイルに

      NET "ROT_A" IFD_DELAY_VALUE = 0;

      こんな記述を追加するらしい。

      では、~CLK を出力して インバータを作るだけで 20 ns 前後 の クロックが生成できそうだ。

      これで CLK2X を作れば... 外部クロックなしで動かせる。ただ、ピンを使うので、ボード依存がないものを作るという目的には合わない。ピンを使うなら 普通に外部クロックを使えば良いし。

      そう言えば、秋月で 48 MHz とかの セラミック発振子を買ったのだった。これを FPGA に直接接続するには、どうしたら良いのだろう?

      追記:
    • MAX7256による水晶発振の実験
    • MAX7256によるCR発振の実験
      という記事を見つけた。... FPGA 側は、インバータひとつで良いらしい。外付けは、並列の R1 (100K) と 直列の R2 (1K)。 (R1 でよく見るのは、1M Ωだったりするがどうなんだろう?)

      秋月で格安だった、SG-645PCP (33MHz) は売り切れてしまった。自分自身はいくつか確保しているが、ひとには薦められない。

      ちょっと 33 MHz は DCM で 使いにくいし、32MHz / 48MHz あたりを基本としようかと思う。

  • SPI ROMプログラマの最初のリリース

      このツールを使ってbitファイルをSPIメモリに直接書き込めば、FPGAが起動します。
      MCSに変換しなくてもよいので、i●PACTを使って書き込むよりも、はるかに手軽です。

    欲しかった情報は、bitファイル を SPI メモリ用に変換できるという事実。

    そもそも bit ファイルというのは、どういう構造をしているのだろう?

      UG332 : 『Spartan-3 ジェネレーション コンフィグレーション ガイド』を読むと、無印の XC3S200 は、

      1 フレーム 1696 bit (212B) x 615 フレーム (計 130380 バイト)

      ついでにメモすると

      XC3S50A : 1 フレーム 1184 bit (148B) x 367 フレーム (計 54316 バイト)
      XC3S200A : 1 フレーム 2208 bit (276B) x 540 フレーム (計 149040 バイト)

      これがメインのデータフレームで、それに 同期ワード、アレイID、 CRC が付く。

      同期ワードは、先頭で、

      3無印/3E 0xAA995566 (32 bits)
      3A 0xAA99 (16 bits)

      アレイID はそれに続き

      XC3S200 : 0xX1414093 (32 bits)
      XC3S50A : 0xX2210093 (32 bits)
      XC3S200A : 0xX2218093 (32 bits)
      (X は Rev. )

      その後データフレームが来て、最後に CRC 。

    それは分かったが、130952 - 130380 = 572 バイトもある。ほかにも情報があるのだろうか?

    それはともかく bit ファイルでは、AA995566 がそのまま見えている。0024: から 01414093 も入っている。だが、mcs ファイルでは、5599AA66 。アレイID は わからない。

    ちょっと B5 からの 0198 と 8019 , 1CC0 と 3803 に着目してみよう。

    00000001 10011000 00011100 11000000
    10000000 00011001 00111000 00000011

    bit の数は同じだが、なにかずれている。よく見れば... バイトの中で bit 順が逆転しているだけのようだ。-- そうなると AA995566 は、

    10101010 10011001 01010101 01100110
    01010101 10011001 10101010 01100110

    -- 5599AA66 になる。

    なんだ、簡単な話ではないか。... SPI FLASH の bit 送出順( MSB First / LSB First ) が逆なだけ?

    確認してみた。

    < 0000: 87 ff ff ff aa 99 55 66 30 00 80 01 00 00 00 07
    ---
    > 0000: ff ff ff ff aa 99 55 66 30 00 80 01 00 00 00 07


    • mcs_test.c
    • bit_test.c

      プログラムは、正常時、全部をダンプするように変更。dump ルーチンに リバースしての表示オプションを追加。

    で、全部の diff を取ると 違うのは先頭だけ。同期ワード前のデータは無視されるはずで違っても関係ない.. と思う。

いろいろ課題はあるが、JTAG の操作に主題を移そう。

    まず、xulaload の xula.py を見てみる。


    # see ug332, Table 9-5 p 207:
    # Spartan-3 Boundary Scan Instructions

    # see ug332, page 340
    def status(self):
    # packet format see ug332, page 323
    read_status = [
    "aa99",
    "2000",
    self.ccl('r', 8, 1),
    "2000",
    "2000",
    ]

    なんてコメントが見える。ug332 は重要なようだ。

    ... ちょっと面倒なので続きは後で。

実装の方針

    まずは、やりたいことを整理しよう。

    • JTAG インターフェイスは、serjtag or FT-232RL 。

    • 対応デバイスは、XC3S50A/200A で後で(= いつか) XC6SLX9 に対応。対応ボードは自前の artemis のみだが、ボード依存性は減らす。あまり汎用化に凝りたくない。

    • .bit/.mcs ファイルを FPGA に直接 コンフィグ
    • .bit/.mcs ファイルを SPI に書く(1)。 インターフェイスは BSCAN を SPI FLASH に接続。
    • .bit/.mcs ファイルを SPI に書く(2)。 インターフェイスは BSCAN(ch1) を rtavr に接続。
    • プログラム(HEX ファイル)を置き換える(1)。インターフェイスは BSCAN(ch2) を ISP に接続。
    • printf デバッグ ができるようにする(1)。インターフェイスは BSCAN(ch1) を rtavr に接続。
    • printf デバッグ ができるようにする(2)。インターフェイスは USART を rtavr に接続。
    • プログラム(HEX ファイル)を置き換える(2)。インターフェイスは BSCAN(ch1) を rtavr に接続しての自己書き換え。
    • プログラム(HEX ファイル)を置き換える(3)。インターフェイスは USARTを rtavr に接続しての自己書き換え。

    デバッグの目的もあるから、冗長なのだがこれぐらいか。

追記: Lattice MachXO2

    Lattice MachXO2 はまだまだ使うつもりはないのだが、いずれ使うかも知れないので、予備調査。

    まず、知りたいのは、Spartan-3A のように JTAG に内部で接続できるかどうか?

    ググるとこの記事が見つかった。XP2 では、JTAGE を使うが XO では JTAGD だそうだ。XO2 は当然ながら書いてない。JTAGF か XO 互換の JTAGD あたり?

      確認できた。JTAGF で、JCAPTURE がない。(lscc/diamond/1.1/cae_library/synthesis/verilog/machxo2.v 参照)


    それはともかく、Spartan-3A のように 2 系統あるようだ。同じように扱えば良さそうで、安心した。

    ちなみに、Lattice の場合、JTAGプライベート命令の IPA(0x32)とIPB(0x38)で切り替えるそうだ。一方 Xilinx は、USER1 と USER2 らしい。

    内蔵FLASH の書き換えは、TN1204 に説明がある。いくつかの方法があるが、JTAG 経由だと、バウンダリスキャンを使って pin を駆動して書き換えることになるのかも。

    書き換え用ファームウェアを config することによる 内蔵FLASH書き換え方法も可能なようだ。これなら Xilinx とほぼ同じ方法が使える。MachXO2 の場合、内蔵オシレータがあるので、クロックについては、Xilinx より自由度が高い。

    ライタについては、Pico では、FT2232D が基板に付いている。FT232R 同様の Bitbang による 操作も可能なはずなので、コードを大幅変更をしなくとも サポートできるはず。

    とりあえず Pico をリファレンスと考えて MachXO2 がサポートできるよう 設計していこう。

プログラム構造の設計

    まず、下位レイヤ 。上記の JTAG インターフェイス をベースした API にする。ただ、送れるビット数に制限があると 面倒なので、上位レイヤが指定したビット数を送るように下位でなんとかする。
    ーそうなると、受信要求の NULL コマンドは上位では扱えない。受信要求を パラメータとして、必要なら 下位で発行するということになるだろう。

    たぶん概念としては、こんな感じ。

    • open()/close()

       初期化と終了

    • set(int tdi, int tms, int tck)

       TDI/TMS//TCK の値設定。

    • put_tdi_bits(uint8_t *buf, int bit_len, int mode , uint8_t *recv_buf)

       TDI にビット列を送り込む。

       mode には、受信データを記録する RECIEVE と
       最後の TMS の状態をセットする TMS_HIGH がある。

    • put_tms_tdi_bits(uint8_t *buf, int bit_len, int mode , uint8_t *recv_buf)

       TDI/TMS の組の データ列を送り込む。

       mode には、受信データを記録する RECIEVE がある。

    • get_tdo_bit(int tdi, int tms)

       TDI/TMS の状態を設定して TDO を読み取る。


    これらをメソッド風に定義して、CABLE ドライバとして、serjtag と ft232r ドライバを作る。あと、JTAG の 面倒を見てくれる サブルーチン群を用意する。

    その上に、Xilinx とかの TARGET の config 方法を知っている TARGET ドライバを構築。

    • config
    • チャネル1 を利用可能にする
    • チャネル2 を利用可能にする

    あたりを TARGET ドライバが メソッドとして提供する。

    config するには、ファイルのロード必要だが、それは別途用意。チャネル1/2 でどういう通信をするかは、いろいろある。ISP を使った書き込みとか、SPI スレーブを使った SPI_FLASH 書き込みとか、SPI_FLASH を直接接続したときの SPI_FLASH 書き込みとかは、メソッド化できるかも知れない。が、だいぶ上位なのでそこまでせずに、関数にしてしまった方が楽そう。

    あと、実をいうと rtavr の エミュレータを接続したい。ISP 機能と SPI スレーブ機能を実装したやつ。SPI を使った printf デバッグみたいなのができるようになると後々楽そう。

    CABLE ドライバに、GPIO の制御 を入れたいのだった。ft232r の場合はピンの割り当ても。
    設計の方針として、『FPGA ボードに触れずに使える』というのをいれていた。

      VS0/VS2 を設定した後、PROG_B に出力して、FPGA を初期化する機能を付ける。INIT_B にも接続しているから、config 失敗も分かる。これらはちゃんとサポートしたい。

      ft232r は Bitbang だから、仕様さえ決まれば対応できる。だが、ピンアサインは、自由にできるようにしておかないと。MachXO2 だとどういう制御をするのかについてもチェックして仕様を決める必要がある。

      一方 serjtag はプロトコルがないから、そこからやらないといけない。自由にピンアサインを決めれるわけでもないので、ピンの config はいらないと思うが、GPIO での制御をする/しないぐらいは、決められないと 汎用のJTAG として使えない。

posted by すz at 12:26| Comment(0) | TrackBack(0) | artemis
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/44862985
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック