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割り当てられている模様. f:id:ThuruThuruToru:20151031232958p:plain

つまり,各制御レジスタのアドレスは以下のようになっている.

  • 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 を表示させる方法も知りたいし,まじめにマニュアルを読もうと思う. f:id:ThuruThuruToru:20151101003706p:plain

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 に続く