水曜日, 3月 27, 2013

JavaでJTAGコンフィギュレーション

どうせやるなら徹底的にということで、今度はJTAGによるFPGAのコンフィギュレーション(回路の書き換え)に挑戦してみたいと思います。JTAGコンフィギュレーションはQuartus IIが出力するSVF形式のファイルを利用します。SVFファイルは一種のスクリプト言語になっていて、これに記述される一連のJTAG操作を実行することにより、回路の再構成が可能となります。

まずは、DE0-Nanoを接続した状態で、Jarファイル(jtag_demo.jar)をダウンロードして実行してみてください。今度は一応64ビット環境も対応しているはずですが、こちらでは未確認です。

jtag_demo.png

上の画像は「Reset FPGA」「IDCODE」「Config FPGA」「Start Demo」の4つのコマンドを順次実行した結果の例です。「Reset FPGA」はFPGAのリコンフィギュレーションを強制的に実行するコマンドで、DE0-Nanoの場合はボード上のEPCS64の回路を読み込みます。「IDCODE」はデバイス(EP4CE22)の32ビットIDCODEを読み出すコマンドです。

「Config FPGA」は、SVFファイルを実行してFPGAのコンフィギュレーションを行います。SVFファイルはJarファイル内にリソースとして格納されていて、元々は1Mバイト超の巨大なファイルだったものが、30Kバイト程度に圧縮されています。実際、Jarファイルの容量の大部分は、同梱されているJNAのJarファイルです。SVF実行は必要最低限の機能しか実装していないので、例えばTDOをチェックするといった機能は省略されています。くれぐれも、DE0-Nano以外のデバイスでは実行しないでください。

最後の「Start Demo」は前回と同様のJTAG通信デモンストレーション(ただし、ボタン押下によるスピード変化や停止はなし)で、入出力データを観察することができます。実は、コンフィギュレーション前でも特にエラーもなく実行できてしまいますが、その場合は出力データがLEDに反映されることはありません。

今度はさすがにSVFの実行やGUIといったそれなりに複雑な処理がありますので、全て一本のJavaプログラムにまとめても読みにくいと思います。そこで、比較的汎用的な部分としてJTAGライブラリと基本的なGUI(JTAG.java)およびSVFの実行(SVFPlayer.java)、そしてデモアプリケーション(JTAG_Demo.java)といった構成に分割しました。

ライブラリを分離したおかげで、デモアプリケーション部分は非常にすっきりしています。JTAG.GUIクラスのインスタンスを生成すると、画面上にボタンが追加されます。実行内容はonClickメソッド内に記述しますが、SVF実行用にJTAG状態遷移メソッドstateと、SHIFT_DR/SHIFT_IR用にshiftメソッドを追加したので、生データを直接writeメソッドで書き込む必要はほとんどなくなりました。SVFPlayerクラスはJTAG.GUIの派生クラスで、生成パラメータにボタン名とSVFのリソース名を指定します。

import info.relm.JTAG;
import info.relm.SVFPlayer;
public class JTAG_Demo extends JTAG {
    public static void main(String[] args) {
        GUI.title("JTAG Demo for DE0-Nano");
        new GUI("Reset FPGA") {
            public void onClick() {
                reset();
                state("RESET", "IRSHIFT");
                shift(10, 0x1);
                state("IREXIT1", "IRUPDATE");
                text.append("Reconfiguration done.\n");
            }
        };
        new GUI("IDCODE") {
            public void onClick() {
                reset();
                state("RESET", "IRSHIFT");
                shift(10, 0x6);
                state("IREXIT1", "DRSHIFT");
                readBytes(4, 0);
                text.append("IDCODE: " +
                    Long.toBinaryString(0xffffffff00000000L |
                    read(4)[0]).substring(32) + '\n');
            }
        };
        new SVFPlayer("Config FPGA", "jtag_led.svf");
        new GUI("Start Demo", "Stop Demo") {
            public void onClick() {
                reset();
                state("RESET", "IRSHIFT");
                shift(10, 0xe);
                state("IREXIT1", "DRSHIFT");
                shift(9, 0x100);
                state("DREXIT1", "DRSHIFT");
                flush();
                try {
                    for (int lfsr = 1; ; Thread.sleep(10)) {
                        readBytes(1, lfsr);
                        shift(1, 1);
                        state("DREXIT1", "DRSHIFT");
                        text.append("out: " +
                            Integer.toBinaryString(lfsr | 256).substring(1) +
                            "\tin: " +
                            Integer.toBinaryString(read(1)[0] | 256).substring(1) +
                            '\n');
                        if (((lfsr <<= 1) & 256) != 0) lfsr ^= 0x171;
                    }
                } catch (InterruptedException e) {}
                shift(9, 0x100);
                state("DREXIT1", "DRUPDATE");
            }
        };
    }
}

これを使えば、わざわざQuartus IIを立ち上げなくてもFPGAのコンフィギュレーションが可能になります。もし問題が無いようでしたら、今後はこの形態でコンフィギュレーションデータを公開していきたいと思います。