More Related Content Similar to Zynq + Vivado HLS入門 (20) Zynq + Vivado HLS入門3. 目次
• 基礎説明
ZYNQ構成
AXIとは
ZYNQ内のAXI経路
VIVADOの特徴
• 実習内容の説明
目標とするデザイン
ツールの説明
ツールフロー
• 実習
VIVADO HLSでmemcpyコアを実装
VIVADO IP integraterでIPコアを接続
SDKでソフトウェアを実装、実行
VIVADO Logic AnalyzerでAXI通信を観察
4. ZYNQ構成
DDR
ZYNQブロック図 *xilinx ホームページより引用
• PS (Processing System)および
PL (Programmable Logic)から
構成される
• PSの構成は以下の通り
Application Processing Unit(APU)
CPU, DMAコントローラ, GIC等
Memory Interface
DDRコントローラ等
IO Peripheral(IOP)
GPIO, UARTコントローラ等
• PS内、PS⇔PL間のインターフェースは
全てARM AXIで統一されている
PS
PL
6. AXIとは
• ARM® AMBA インターフェースのこと※AXI4はAMBA第4世代インターフェース
• MasterとSlaveが1対1で通信を行う
• スイッチを介すことで、複数Masterと複数Slaveを
繋げることもできる.(しかし通信は1対1)
• Slaveはレジスタもしくはメモリを持っていて、
Masterはアドレスを指定してデータの読み書きを行う
M
S
0x00000000
0x0000ffff
12. ZYNQ DMAエンジン
S
M
DMA ⇔ DDR間
Master => DMA
Slave => DDR
DMA ⇔ AXIGP(Slave)間
Master => DMA
Slave => AXIGPS
21. VIVADO HLS "AXIHP MEMCPYコア"
AXIHP_MEMCPY
(created by VIVADO HLS)
interrupt
clk
reset
AXILite AXIMS
32bit 64bit
34. VIVADO HLS “axihp_memcpyの実装2/4”
typedef unsigned long long u64;
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out);
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out)
{
}
• axihp(64bit)用の入力, 出力ポートを関数の引数に記述
• “unsigned long long”は長いので”u64”に置き換える
35. VIVADO HLS “axihp_memcpyの実装3/4”
typedef unsigned long long u64;
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out);
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out)
{
u64 buf[256];
memcpy(buf, axihp_in, 256 * sizeof(u64));
}
• 入力データを受け取るための配列”buf”を用意する
• burst転送はmemcpyを使用して表現する
バースト長256のバースト転送(受信)となる
※buf[0] = *axihp_in; とすると通常転送の読み出しとなる
36. VIVADO HLS “axihp_memcpyの実装4/4”
typedef unsigned long long u64;
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out);
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out)
{
u64 buf[256];
memcpy(buf, axihp_in, 256 * sizeof(u64));
memcpy(axihp_out, buf, 256 * sizeof(u64));
}
• memcpyでバースト転送の書き込みを記述する
バースト長256のバースト転送(送信)となる
※*axihp_out = buf[0]; とすると通常転送の書き込みとなる
37. VIVADO HLS “axihp_memcpy関数”
typedef unsigned long long u64;
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out);
void axihp_memcpy(u64 *axihp_in, u64 *axihp_out)
{
u64 buf[256];
memcpy(buf, axihp_in, 256 * sizeof(u64));
memcpy(axihp_out, buf, 256 * sizeof(u64));
}
• 以上でaxihp_memcpy関数は完成となる
• AXIHPの読み出し/書き込みアドレスおよび
axihp_memcpyの制御方法は[Directive]で設定していく
40. VIVADO HLS “Directive設定3/10”
• [Directive]のプルダウンメニューを”INTERFACE”にする
• [mode]のプルダウンメニューを”m_axi”にする
• [offset]のプルダウンメニューを”slave”にする
• [OK]をクリック
1
2
3
4
この設定を”slave”にすると読み
出しアドレスを、slaveポート
から指定できるようになる
“axihp_in”はaxiのmaster
ポートと認識されるようになる
43. VIVADO HLS “Directive設定6/10”
• [Directive]のプルダウンメニューを”INTERFACE”にする
• [mode]のプルダウンメニューを”m_axi”にする
• [offset]のプルダウンメニューを”slave”にする
• [OK]をクリック
1
2
3
4
この設定を”slave”にすると読み
出しアドレスを、slaveポート
から指定できるようになる
“axihp_out”はaxiのmaster
ポートと認識されるようになる
46. VIVADO HLS “Directive設定9/10”
• [Directive]のプルダウンメニューを”INTERFACE”にする
• [mode]のプルダウンメニューを”s_axilite”にする
• [OK]をクリック
1
2
3
この設定により
“axihp_memcpy”はaxi slave
ポートから制御される.
また終了を知らせるinterrupt
信号も生成される.
51. VIVADO HLS “IP Packageing1/3”
• axihp_memcpyをIPパッケージ化していく
• [Export RTL]をクリックする
1
52. VIVADO HLS “IP Packaging 2/3”
• “Verilog”または”VHDL”を選択
• [OK]をクリックする
1
2
53. VIVADO HLS “IP Packaging 3/3”
• IPパッケージ化が完了した
• 以上でVIVADO HLSによる操作は終了となる
72. VIVADO “PSの設定2/7”
• [MIO Configuration] タブを選択クリック
• [I/O Peripherals]を開く
• [UART1] にチェックを入れ, MIO 48, 49が選択されていることを確認
2
1
3
3
73. VIVADO “PSの設定3/7”
• [DDR Configuration] タブをクリック
• [DDR Controller Configuration]を開く
• ZedBoardの場合、Memory Partを”MT41J128M16 HA-15E”に変更
• ZYBOの場合、Memory Partを”MT41J128M16JT-125”に変更
1
2
3
※ツールのバージョンによっては”MT41K128M16JT-125”
74. VIVADO “PSの設定4/7”
• [Clock Configuration]タブを選択
• ZedBoardの場合、Input Frequencyを”33.333333”にする
• Zyboの場合、Input Frequencyを”50”にする
• [PL Fablic Clocks]タブを開く
• FCLK_CLK0の周波数を”100”に設定する
1
2
3
4
101. VIVADO “Generate Block Design 1/3”
• Block Designに含まれるIPに必要な制約、
デザインのネットリストを生成する
• [Generate Block Design]をクリックする
1
108. • [OK] をクリック
• Exportが完了したらBitstreamを生成する
VIVADO “SDK Export2/2”
1
112. SDK “SDKの起動1/2”
• SDKを起動する (linuxの場合 “xsdk” コマンドで起動)
• VIVADOは”project_dir/project_name.sdk”にHardware情報を
Exportしている
• Workspaceは”zynq_vivadohls/zynq_vivadohls.sdk”を選択する
• [OK]をクリック
1
2
113. SDK “SDKの起動2/2”
• SDKの起動が完了した
• design_1_wrapper_hw_platform_0内にExportされた
hardware情報やPSの設定スクリプトがある
(バージョンが2014. 2の場合、hardware Platform Specificationを自分で作成する必要あり)
114. SDK “Board Support Packageの生成1/4”
• [New]をクリック
• [Xilinx]タブを開く
• [Board Support Package]を選択
• [Next]をクリックする
1
2
3
4
117. SDK “Board Support Packageの生成4/4”
• Board Support Packageが生成された
• standalone_bsp_0内にはxilinxが提供するlibraryや
hardwareのアドレスを定義したヘッダーファイル等がある
125. SDK “HLS コア 制御アプリケーション雛型”
#include "xil_printf.h"
typedef unsigned long long u64;
int main()
{
int i, mismatch = 0;
volatile u64 src_data[256], dst_data[256];
for (i = 0; i < 256; i++)
src_data[i] = i; //create source data
//control hls core
for (i = 0; i < 256; i++) {
xil_printf("src_data[%d] = %d, ", i, src_data[i]);
xil_printf("dst_data[%d] = %dnr", i, dst_data[i]);
if (src_data[i] != dst_data[i]) mismatch = 1; //compare src_data and dst_data
}
(mismatch == 0) ? xil_printf("memcpy success!nr"):
xil_printf("memcpy failnr");
return 0;
}
• axihp_memcpyコアにsrc_dataの値をdst_dataにコピーさせる
• 以降、この雛型に赤文字で書かれたプログラムを追加していく
126. SDK “キャッシュの無効化”
#include "xil_cache.h"
…
int main()
{
Xil_DCacheDisable(); //Disable Data Cache
…
return 0;
}
• xil_cache.h内で定義されているXil_DCacheDisable関数を使うと
データキャッシュをオフにすることができる
• DMAを正しく実行するにはflushおよびinvalidateを適切に行う必要
がある.簡単のため、今回はキャッシュを無効化する
128. SDK “axihp_memcpyコアのレジスタ構成2/2”
0x00 Control signals
0x04
Global Interrupt
Enable
0x08
IP Interrupt
Enable
0x0c
IP Interrupt
Status
0x10
Data signal of
axihp_in
0x14 reserved
0x18
Data signal of
axihp_out
0x1c reserved
読み出しアドレス
書き込みアドレス
• bit0: ap_start
• bit1: ap_done
• bit2: ap_idle
• bit3: ap_ready
• bit7: auto_restart
• others: reserved
axihp_memcpyレジスタ(32bit)
130. SDK “読み出し/書き込みアドレスの設定”
…
unsigned int *baseaddr = 0x43c00000; //reg0 of hls core
int main()
{
…
//control hls core
baseaddr[4] = src_data; //set read addr
baseaddr[6] = dst_data; //set write addr
…
}
• 0x43c00000番地がaxihp_memcpyのレジスタ0の番地となる
131. SDK “axihp_memcpyの開始制御”
…
int main()
{
…
//control hls core
baseaddr[4] = src_data; //set read addr
baseaddr[6] = dst_data; //set write addr
if ((baseaddr[0] & 0x4) == 0x4) { //if ap_idle is high
xil_printf("memcpy startnr");
baseaddr[0] |= 0x1; //make ap_start high
}
…
}
• ap_idleが1であればap_startを1にして開始する
132. SDK “axihp_memcpyの終了制御(polling版)”
…
int main()
{
…
//control hls core
…
if ((baseaddr[0] & 0x4) == 0x4) {//if ap_idle is high
xil_printf("memcpy startnr");
baseaddr[0] |= 0x1; //make ap_start high
}
while ((base_addr[0] & 0x4) == 0); //wait for memcpy(polling)
xil_printf("memcpy done!nr");
…
}
• ap_idleが再び1になるまでwhile文で待機する
133. SDK “axihp_memcpyソフトウェア”
#include "xil_cache.h"
#include "xil_printf.h"
typedef unsigned long long u64;
unsigned int *baseaddr = 0x43c00000;//reg0 of hls core
int main()
{
Xil_DCacheDisable(); //Disable Data Cache
int i, mismatch = 0;
volatile u64 src_data[256], dst_data[256];
for (i = 0; i < 256; i++)
src_data[i] = i; //create source data
//control hls core
baseaddr[4] = src_data; //set read addr
baseaddr[6] = dst_data; //set write addr
if ((baseaddr[0] & 0x4) == 0x4) { //if ap_idle is high
xil_printf("memcpy startnr");
baseaddr[0] |= 0x1; //make ap_start high
}
while ((baseaddr[0] & 0x4) == 0); //wait for memcpy(polling)
xil_printf("memcpy done!nr");
for (i = 0; i < 256; i++) {
xil_printf("src_data[%d] = %d, ", i, src_data[i]);
xil_printf("dst_data[%d] = %dnr", i, dst_data[i]);
if (src_data[i] != dst_data[i]) mismatch = 1; //compare src_data and dst_data
}
(mismatch == 0) ? xil_printf("memcpy success!nr"):
xil_printf("memcpy failnr");
return 0;
}
プログラム全体は以下のようになる
138. SDK “アプリケーション実行の前に”
• アプリケーションの実行結果はuartで出力される
• SDK上で結果を確認することもできるが、不具合が多い(linuxの場合)
したがってscreen, cuコマンドやminicomでシリアルポートに接続する
>ls /dev/ttyACM*
ttyACMx
>sudo chmod 666 /dev/ttyACMx
>cu -s 115200 -l /dev/ttyACMx
cuコマンドの例(ZedBoardの場合)
>ls /dev/ttyACM*
ttyACMx
>sudo chmod 666 /dev/ttyACMx
>screen /dev/ttyACMx 115200
screenコマンドの例(ZedBoardの場合)
140. • [Xilinx C/C++ application (GDB)] を右クリック
• [New]をクリックする
1
2
SDK “アプリケーションの実行2/3”
145. SDK "割り込みプログラム1/4"
#include "xparameters.h"
#include "xscugic_hw.h"
#include "xil_exception.h"
…
int main()
{
…
//control hls core
unsigned int device_id = 0;
XScuGic_DeviceInitialize(device_id);
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
XScuGic_DeviceInterruptHandler, device_id);
Xil_ExceptionEnable();
XScuGic_RegisterHandler(XPAR_PS7_SCUGIC_0_BASEADDR, 61,
axihp_memcpy_handler, NULL);
baseaddr[2] |= 0x1;
baseaddr[1] = 0x1;
XScuGic_EnableIntr(XPAR_PS7_SCUGIC_0_DIST_BASEADDR, 61);
…
}
146. SDK "割り込みプログラム2/4"
#include "xparameters.h"
#include "xscugic_hw.h"
#include "xil_exception.h"
…
volatile int memcpy_done = 0;
void axihp_memcpy_handler();
int main()
{
…
//while ((baseaddr[0] & 0x4) == 0); //wait for memcpy
while (memcpy_done == 0); //wait for interrupt
…
}
void axihp_memcpy_handler()
{
baseaddr[2] ^= 0x1;
xil_printf("interrupt!nr");
memcpy_done = 1;
baseaddr[3] = 0x1;
baseaddr[2] |= 0x1;
}
148. SDK "割り込みプログラム4/4"
#include "xparameters.h"
#include "xscugic_hw.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "xil_printf.h"
typedef unsigned long long u64;
unsigned int *baseaddr = 0x43c00000;//reg0 of hls core
volatile int memcpy_done = 0;
void axihp_memcpy_handler();
int main()
{
Xil_DCacheDisable(); //Disable Data Cache
int i, mismatch = 0;
volatile u64 src_data[256], dst_data[256];
for (i = 0; i < 256; i++)
src_data[i] = i; //create source data
//control hls core
unsigned int device_id = 0;
XScuGic_DeviceInitialize(device_id);
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
XScuGic_DeviceInterruptHandler, device_id);
Xil_ExceptionEnable();
XScuGic_RegisterHandler(XPAR_PS7_SCUGIC_0_BASEADDR, 61,
axihp_memcpy_handler, NULL);
baseaddr[2] |= 0x1;
baseaddr[1] = 0x1;
XScuGic_EnableIntr(XPAR_PS7_SCUGIC_0_DIST_BASEADDR, 61);
baseaddr[4] = src_data; //set read addr
baseaddr[6] = dst_data; //set write addr
if ((baseaddr[0] & 0x4) == 0x4) { //if ap_idle is high
xil_printf("memcpy startnr");
baseaddr[0] |= 0x1; //make ap_start high
}
//while ((baseaddr[0] & 0x4) == 0); //wait for memcpy(poling)
while (memcpy_done == 0); //wait for interrupt
xil_printf("memcpy done!nr");
for (i = 0; i < 256; i++) {
xil_printf("src_data[%d] = %d, ", i, src_data[i]);
xil_printf("dst_data[%d] = %dnr", i, dst_data[i]);
if (src_data[i] != dst_data[i]) mismatch = 1;
//compare src_data and dst_data
}
(mismatch == 0) ? xil_printf("memcpy success!nr"):
xil_printf("memcpy failnr");
return 0;
}
void axihp_memcpy_handler()
{
baseaddr[2] ^= 0x1;
xil_printf("interrupt!nr");
memcpy_done = 1;
baseaddr[3] = 0x1;
baseaddr[2] |= 0x1;
}
プログラム全体は以下のようになる
150. VIVADO "Hardware Manager の起動1/6"
• [Open Hardware Manager]タブを開く
• [Open Target]をクリック
• [Open New Target]をクリック
1
2
3
163. 寄り道 "CLKが出力されない6/6"
XMD% connect arm hw
XMD% rst –slcr
XMD% cd "project_name".sdk/designe_1_wrapper_hw_platform_0
XMD% source ps7_init.tcl
XMD% ps7_init
XMD% ps7_post_config
XMD% disconnect 64
• SDKのxmdコンソールでps7_post_configを実行する方法
173. Next Step "RTLでAXI State Machine書きたい!!1/5"
• AXIの雛型の作り方
• VIVADOのProject Setteingsで"Vendor"を設定する
177. Next Step "RTLでAXI State Machine書きたい!!5/5"
"…/zynq_vivadohls/../ip_repo/"ip名"/hdl/"に生成したIPの
ソースファイルが格納されている
AXIステートマシンが記述されているので、このファイルを改造
していくのが良いと思われる
DigilentのZYBOのチュートリアル前半部分が似たような内容になっている
(http://digilentinc.com/Data/Products/ZYBO/ZYBO-Embedded_Linux_Hands-on_Tutorial.pdf)