ZYNQ MPSoC向けにxsdkとILAでのデバッグをする話

Pocket

ZYNQ MPSoCでxsdkを使ったソフトウェア開発と、ILA使ったハードウェア開発を行う時の方法がわからないという話を聞いたので、普通にILAやVIOが使えますよ、というメモ。

開発環境はVivado 2019.1.3、ターゲットはTrenz TE0802-02です。

ハードウェアの準備

まずは適当にハードウェアを用意します。

トップモジュールはこんな感じ。

`timescale 1 ps / 1 ps

module design_1_wrapper
   (LED);
  output LED;

  wire [7:0] LED;
  wire pl_clk0_0;
  wire pl_resetn0_0;

  design_1 design_1_i
       (.pl_clk0_0(pl_clk0_0),
        .pl_resetn0_0(pl_resetn0_0));
        
  reg [31:0] counter;
  
  always @(posedge pl_clk0_0)
  begin
    counter <= counter + 1;
  end
  assign LED = counter[27:20];
  
  ila_0 ila_0_i (
	.clk(pl_clk0_0), // input wire clk
	.probe0(counter) // input wire [31:0] probe0
  );
  
endmodule

内部で32bitのカウンタを回して[27:20]をLEDに出力しています。動作確認のために、カウンタの値をILAを使って読み出すようにしています。

Generate Bitstream でビルドすると、

  • プロジェクト名/プロジェクト名.runs/impl_1/design_1_wrapper.bit
  • プロジェクト名/プロジェクト名.runs/impl_1/design_1_wrapper.ltx

ができあがります。

xsdk向けにプロジェクトをエクスポートします。

Export Hardwareのダイアログでは、include bitstreamにチェックを入れておきましょう。エクスポート先(Export to:)はデフォルトのままでOKです。

ソフトウェアを用意する

適当にソフトウェアを用意します。File -> New -> Application Projectをクリックして Templates の Hello World を選択してみます。

たとえば、hello_worldという名前のプロジェクトをつくってみます。

テンプレートで、Hello Worldを選びました。

テンプレートで生成されたプログラムをちょっといじって、ソフトウェアの処理が長引くように変更します。たとえばこんな感じ。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"


int main()
{
    init_platform();

    int i = 0;
    for(i = 0; i < 300; i++){
    	print("Hello World\n\r");
    	xil_printf("%d\n\r", i);
    	sleep(1);
    }
    cleanup_platform();
    return 0;
}

で、ツールバーのハンマーのアイコンでビルドします。

とりあえず動かす

まずは、xsdkのいつもの手順で動かします。メニューの Xilinx -> Program FPGAで FPGAにbitファイルを流しこみます。

次に、ソフトウェアプロジェクトの Binaries を展開、hello_world.elf を右クリックして Run as -> Launch on Hardware (System Debugger) でプログラムの実行が開始されます。

Hardware Managerを起動する

Vivadoで Open Hardware Manager をクリックして Hardware Manager を起動します。ローカルで接続していれば、Open TargetでFPGAに接続できます。

ハードウェア開発に使ったプロジェクトで Hardware Mangaer を開くと、たいていの場合、うまくILAに対応したltxファイルを使ってFPGA内部の情報をプローブすることができます。

もし、単体で Hardware Manager を起動したり、うまくILAとの対応が取れない場合には、Hardware のツリーでFPGA(armじゃない方)を選択して Hardware Device Properties ペインを表示、Probes fileにデバッグに利用したいltxファイルを選択します。

VIOもつかってみる

PSから入出力するためのGPIOを追加してみましょう。

トップモジュールはこんな感じに。PSからGPIOでアクセスされる先にVIOを接続しています。

`timescale 1 ps / 1 ps

module design_1_wrapper
   (LED);
  output [7:0] LED;
  
  wire [7:0] LED;
  
  wire [31:0]GPIO_0_tri_o;
  wire [31:0]GPIO_1_tri_i;
  wire pl_clk0_0;
  wire pl_resetn0_0;

  design_1 design_1_i
       (.GPIO_0_tri_o(GPIO_0_tri_o),
        .GPIO_1_tri_i(GPIO_1_tri_i),
        .pl_clk0_0(pl_clk0_0),
        .pl_resetn0_0(pl_resetn0_0));

  assign LED = GPIO_0_tri_o[7:0];

  vio_0 vio_0_i (
    .clk(pl_clk0_0),           // input wire clk
    .probe_in0(GPIO_0_tri_o),  // input wire [31 : 0] probe_in0
    .probe_out0(GPIO_1_tri_i)  // output wire [31 : 0] probe_out0
  );

endmodule

Generate bitstreamでビットファイルを作成、xsdkにエクスポートします。

ソフトウェア側の変更

ソフトウェア側にはGPIOの読み書きを追加します。手を抜いて、生アドレスでアクセスしてます。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"


int main()
{
    init_platform();

    unsigned int * gpio0 = (unsigned int*)0x0080000000L;
    unsigned int * gpio1 = (unsigned int*)0x0080001000L;


    int i = 0;
    for(i = 0; i < 300; i++){
    	print("Hello World\n\r");
    	xil_printf("%d\n\r", i);
    	*gpio0 = i;
    	xil_printf("%08x\n\r", *gpio1);
    	sleep(1);
    }
    cleanup_platform();
    return 0;
}

ビルド hello_world.elf を更新します。

実行

同じように、メニューバーの Xilinx -> Program FPGAでダイアログを開いてFPGAをコンフィギュレーションします。ソフトウェアの実行はhello_world.elfを右クリックして Run as -> Launch on Hardwareです。

VIOの操作

VIOを操作するためにVivadoでHardware Managerを起動します。うまくltxがアタッチされていなければ明示的にProbes fileにltxファイルを選択します。

hw_vio_1を選択してモニタペインを開き、+ボタンをクリックしてプローブを追加します。ここでは、GPIO_0_tri_oもGPIO_1_tri_iも両方選択してOKをクリックします。

VIOのプローブを開いたところです

1秒に1回インクリメントしてる様子がGPIO_0_tri_oで見て取れます。また、GPIO_1_tri_iには初期値のDEAD_BEEFがセットされています。

シリアルポートを開いているターミナルはこんな感じ。DEADBEEFが読み出せてますね。

VIOでGPIO_1_tri_iの値をABADCAFEに変更してみます。ターミナル上の表示が更新されることが確認できます。

まとめ

xsdkを使ったソフトウェアを開発とVivadoでILAを使ってハードウェアを開発を同時に行なう場合にも、ILAやVIOが使える様子を紹介しました。