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が使える様子を紹介しました。