FPGA をもっと活用するために IP コアを使ってみよう (2)

みなさんこんにちは。この「FPGA をもっと活用するために IP コアを使ってみよう」のシリーズでは、全 5 回を通じて FPGA を使って実用的なアプリケーションを実装するために必要不可欠な IP コアの使い方を紹介していきます。

第 2 回の今回は、FPGA 内部に仮想的なスイッチを挿入できる IP コア VIO の紹介と使い方を紹介します。ちょっとした動作の条件を操作したい場合や、複雑な回路をデバッグする際にとっても役に立つ IP コアです。

このコースの前の記事はこちら

VIO コアって何?

FPGA を使うときには、JTAG というインターフェースを通じてコンフィギュレーションします。この JTAG を使って動作中の FPGA 内部のレジスタの値を読み書きする機能を提供するIPコアが VIO (Virtual Input/Output) (PG159)です。

自分で同等の機能を用意しようと思うと、 FPGA の仕組みや JTAG の仕組みを詳しく知る必要がありますが、IP コアとして用意されている VIO は手軽に利用できます。また、Vivado のハードウェアモニタ機能ビューと連携して便利に使えます。

使ってみよう!!

シリアル通信で Hello, FPGA (1) の onoff_circuit.sv を題材に VIO の使い方をステップ・バイ・ステップで紹介していきます。ターゲットボードは Digilent の Arty です。I/O 割当を含むソースコードは Gist からダウロードできます。

次のコードは、onoff_circuit.sv の入力のスイッチ ( SW_ON、SW_OFF ) を内部信号に変更したものです。ここで、logic の前についている (* KEEP *) というのは、ツールに対する指示子 ( プログラミング言語 C の #pragma みたいな ) です。ツールに KEEP を付けると変数名を残すことを指示できます。

module onoff_circuit (
    input  logic CLK, RST,
    output logic ON
    );

    // 入力から内部信号に変更。logicの前の (* KEEP *) で信号を残すようツールに指示
    (* KEEP *) logic SW_ON, SW_OFF; 
    
    typedef enum {
        STATE_OFF,
        STATE_ON
    } state_type;
    state_type state, n_state;

    always_comb begin
        ON      = 1'b0;
        n_state = state;
        if (state == STATE_OFF) begin
            if (SW_ON & ~ SW_OFF) begin
                ON      = 1'b1;
                n_state = STATE_ON;
            end
        end else if (state == STATE_ON) begin
            if (SW_OFF) begin
                n_state = STATE_OFF;
            end
        end
    end

    always_ff @ (posedge CLK) begin
        if (RST) begin
            state <= STATE_OFF;
        end else begin
            state <= n_state;
        end
    end
endmodule

このデザインを FPGA で動かしても、このままでは SW_ON と SW_OFF を変更できません。この SW_ON と SW_OFF に VIO を接続することで仮想的に操作できるようにしましょう。手順は次の通りです。

  1. カタログから使いたい IP コアを選び、パラメタを設定してモジュールを生成する
  2. SystemVerilog コード内に作ったモジュールのインスタンスを作り、読み書きしたい信号を接続する

ソフトウェアプログラムでいうと、1. でクラスを作って、2. でそのインスタンスを作るというステップに相当します。順を追って試していきましょう。

IPコアを選んでモジュールを生成する

IP コアのカタログは、Vivado の Flow Navigator の IP Catalog をクリックすると表示できます。

検索ボックスに vio と入力すると、今回使用したい VIO コアが表示されます。そこをダブルクリックします。

設定ダイアログでは、モジュールの名前と IP コアで設定が可能なパラメタの値を決めます。今回の名前は vio_0 のままでよいでしょう。VIO は入力と出力を設定できます。VIO の入力は FPGA 内部の信号を JTAG 経由で引き出すための信号、出力は JTAG 経由で FPGA 内部のレジスタに値を書き込むためのポートです。ここでは、ONの信号を観測するためにビット幅1の入力ポートと、FPGA 内の SW_ON と SW_OFF の二つの値を変更するビット幅2の出力ポートを有効にします。

OK をクリックして設定ダイアログを閉じると、IP コアの合成を確認するダイアログが開きます。Generate をクリックしてステップをすすめます。

Sources ペインの Hierarchy タブに、vio_0 のモジュールが登録されていることが確認できます。

VIOモジュールのインスタンスを生成する

使用したい IP コアのモジュールが用意できたので、このインスタンスをデザインに組み込みます。IP コアのモジュールを作成すると、インスタンス生成のためのテンプレートも生成されます。これをコピペすることができます。

Sources ペインの IP Sources タブを開き、モジュールのツリーから .veo という拡張子のついたテンプレートファイルにアクセスします。

このインスタンス生成の部分を元の SystemVerilog ソースコードに貼りつけます。

入出力ポートに読み書きしたい信号を接続します。今回は、VIO の出力を SW_ON と SW_OFF に、VIO への入力に ON を接続します。

module onoff_circuit (
    input  logic CLK, RST,
    output logic ON
    );
    
    // 入力から内部信号に変更。logicの前の (* KEEP *) で信号を残すようツールに指示
    (* KEEP *) logic SW_ON, SW_OFF; 
    
    typedef enum {
        STATE_OFF,
        STATE_ON
    } state_type;
    state_type state, n_state;

    always_comb begin
        ON      = 1'b0;
        n_state = state;
        if (state == STATE_OFF) begin
            if (SW_ON & ~ SW_OFF) begin
                ON      = 1'b1;
                n_state = STATE_ON;
            end
        end else if (state == STATE_ON) begin
            if (SW_OFF) begin
                n_state = STATE_OFF;
            end
        end
    end

    always_ff @ (posedge CLK) begin
        if (RST) begin
            state <= STATE_OFF;
        end else begin
            state <= n_state;
        end
    end

    // VIO との入出力と接続する信号を定義
    logic [0:0] probe_in0;
    logic [1:0] probe_out0;
    
    // テンプレート(.veoファイル)をコピペして VIO のインスタンスを生成する
    vio_0 vio_0_i_0 (
        .clk(CLK),
        .probe_in0(probe_in0),
        .probe_out0(probe_out0)
    );
    
    // ON を probe_in0 を介して外で読み出せるように接続
    assign probe_in0[0] = ON;
    
    // probe_out0を介して外からSW_ONとSW_OFFの値を設定できるように接続
    assign SW_ON  = probe_out0[1];
    assign SW_OFF = probe_out0[0];

endmodule

読み書きしてみる

以上で、VIO をデザインに組み込むフローは終了です。あとは、いつものように、Generate Bitstream をクリックして、FPGA のコンフィギュレーションファイル ( bit ファイル ) を作成します。

bit ファイルの作成がおわったら Hardware Manager を開いて FPGA に書き込みます。このとき、VIO コアに付随して生成される ltx ファイルも同時にセットします ( bitファイルを選択すると対応する ltx ファイルも自動的に選択されるはず)。ltx ファイルにはコンフィギューションされた FPGA に Hardware Manager からアクセスするための情報が書き込まれています。

コンフィギュレーションが終わると、VIO インスタンスを操作するためのユーザインターフェースである hw_vio_1 というエントリがあらわれます。ここで + ボタンをクリックすると読み書きしたい信号を選ぶことができます。今回は全部選択して OK をクリックします。

読み書きの準備が整いました。SW_ON や SW_OFF の隣りの 0 を 1 あるいは 1 を 0 に変更すると、ON_OBUF という項目の値が変化する様子を確認できます。

手元にボードがあれば、ON_OBUF の値と LED が連動している様子も確認できるはずです。逆に言えば、手元にないボードの LED の様子をうかがい知ることができるわけです。

セレクトリストの代わりにボタンを選択することができます。たとえば Toggle Button を選択するとセレクトリストがボタンのアイコンに変わります。

ボタンをクリックするたびに 0 と 1 が反転し、それに応じて ON_OBUF の値も変化します。

まとめ

「IP コアを活用すると FPGA 開発が楽になる」事例の一つとして、FPGA 内部の信号を読み書きできる IPコア VIO を紹介し、サンプルコードを例に VIO をデザインに組み込む手順を説明しました。基本的には、Xilnx が提供する他の IP コアも同じような手順でデザインに組み込むことができます。IP カタログには実に様々な IP コア が登録されているので、いろいろ試してみるのも面白いかもしれません。

今回紹介した VIO コアはソフトウェアプログラミングにおける printf のようにデバッグのときに便利な存在です。この連載の次回以降の記事でも度々利用していきますのでお楽しみに。

わさらぼ・みよしたけふみ

タイトルとURLをコピーしました