ZYBOで遊ぶ01:簡易タイマーIPの自作(3)
ZYBOで遊ぶ01:簡易タイマーIPの自作(2) - ThuruThuruToru’s blog の続き
ZYBOで遊ぶ01:簡易タイマーIPの自作(1) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(2) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(3) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(4) - ThuruThuruToru’s blog
8. Hello World をHost PC上のターミナルに表示
8.1. Application Projectの作成
まずは,Application Projectを作成し,Templatesのコードを実行してみる.
File->New->Application Projectをクリックする.
Project nameをtestとし,Nextをクリック.Templatesに Hello Worldを選択し,Finishをクリック.
Project Explorerにtestとtest_bspが作成される.
Project Explorer->test->src->helloworld.cをダブルクリックする.
main関数内に UARTを使ってHost PC上のターミナルに"Hello World"を表示するコードが書かれている.
8.2. Hello Worldを実行してみる
ソースを編集すると自動でビルドされるようになっているようなので,helloworld.cを一部変更->元に戻して保存し,実行ファイルを生成する.
ZYBOの電源が入っており,PCとつながっている状態で,Xilinx Tools->Program FPGAをクリック.
Programボタンを押せば,FPGAに構成情報が書き込まれる.
下部にあるTerminalタブ内のSettingsをクリックし,Connection TypeをSerial,Baud Rateを115200に設定(Portも確認). cuコマンドをxterm等の自分の好きなターミナルで実行してシリアル接続しても良い.
Run->Debug As->2 Launch on Hardware(System Debugger)をクリック.
Debug画面になるので,上部にあるResumeボタンをクリックするとプログラムが実行され,"Hello World"がTerminalに表示される.
9. 自作Timerを動かす
9.1. アドレスマップ
実はVivadoのBlock Diagram内のAddress Editorタブ内でmyTimerのアドレスマップの割り当てを決めることができる.今回は自分でいじっていないのでデフォルトで 0x43C0_0000から64K割り当てられている模様.
つまり,各制御レジスタのアドレスは以下のようになっている.
- 0x43c0_0000: [割り込みステータス]
- 0x43c0_0004: [ステータス/コントロール]
- 0x43c0_0008: [カウンタ値]
- 0x43c0_000C: [デバッグ0]
BASEADDRESSである0x43C0_0000は,
Project Explorer->test_bsp->ps7_cortexa9_0->include内のxparameters.h に自動でdefineされるようなのだがなぜか記述がない・・・
色々試して見たところ,system.mss の"Modify this BSP's Settings"をクリックし,Overview->driversのmyTimerのDriver列の値をgenericに変更したところ,xparameters.h内にBASEADDRESSの定義が追記された.
#define XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR 0x43C00000 #define XPAR_MYTIMER_0_S00_AXI_LITE_HIGHADDR 0x43C0FFFF
自分で定義しても良いが,何か気持ち悪い.
system.mss の自作IPの所に他のIPみたいに Documentation, Import Examples を表示させる方法も知りたいし,まじめにマニュアルを読もうと思う.
9.2. 制御レジスタへのアクセステスト
xil_io.h に定義されている,Xil_Out32, Xil_In32で制御レジスタのWrite/Readができた.
Xil_Out32のコードを見てみると,
*(volatile u32 *) OutAddress = Value;
となっており,単純に指定されたアドレスに値を入れている.
これでAXI-Liteのライトが発行されている模様.
myTimerの[デバッグ0]レジスタに値を書いて,その値が読めることを確認するコードは以下の通り.
#include <stdio.h> #include "platform.h" #include "xil_io.h" #define MAX_SIZE 1024 #define MYTIMER_INT_STAT_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x0 #define MYTIMER_STAT_CTRL_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x4 #define MYTIMER_COUNTER_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x8 #define MYTIMER_DEBUG0_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0xC int main() { u32 read_data; char str[MAX_SIZE]; init_platform(); print("Hello World\n\r"); // write/read myTimer[DEBUG0] Xil_Out32(MYTIMER_DEBUG0_ADDR, 0x12345678); read_data = Xil_In32(MYTIMER_DEBUG0_ADDR); snprintf(str, MAX_SIZE, "%lx\n\r", read_data); print(str); cleanup_platform(); return 0; }
9.3. 自作Timerの動作確認
下記のコードでタイマーを動かし,各制御レジスタの値を観察してみた.
#include <stdio.h> #include "platform.h" #include "xil_io.h" #define MAX_SIZE 1024 #define MYTIMER_INT_STAT_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x0 #define MYTIMER_STAT_CTRL_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x4 #define MYTIMER_COUNTER_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x8 #define MYTIMER_DEBUG0_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0xC u32 read_and_print(u32 Addr, char *head_str) { u32 read_data; char str[MAX_SIZE]; read_data = Xil_In32(Addr); snprintf(str, MAX_SIZE, "%s = %lx\n\r", head_str, read_data); print(str); return read_data; } int main() { u32 read_data; init_platform(); read_and_print(MYTIMER_INT_STAT_ADDR, "[INT_STAT]"); read_and_print(MYTIMER_STAT_CTRL_ADDR, "[STAT_CTRL]"); read_and_print(MYTIMER_COUNTER_ADDR, "[COUNTER]"); // run timer print("Run Timer------------------------------------------\n\r"); Xil_Out32(MYTIMER_COUNTER_ADDR, 0x10000000); Xil_Out32(MYTIMER_STAT_CTRL_ADDR, 0x00000001); do { read_and_print(MYTIMER_INT_STAT_ADDR, "[INT_STAT]"); read_and_print(MYTIMER_COUNTER_ADDR, "[COUNTER]"); read_data = read_and_print(MYTIMER_STAT_CTRL_ADDR, "[STAT_CTRL]"); print("\n\r"); } while(read_data==1); print("---------------------------------------------------\n\r"); read_and_print(MYTIMER_INT_STAT_ADDR, "[INT_STAT]"); read_and_print(MYTIMER_STAT_CTRL_ADDR, "[STAT_CTRL]"); read_and_print(MYTIMER_COUNTER_ADDR, "[COUNTER]"); cleanup_platform(); return 0; }
ZYBOで遊ぶ01:簡易タイマーIPの自作(4) - ThuruThuruToru’s blog に続く