プログラムの作成 (LIFEゲーム)


今回はプログラムの一例としてライフゲームを取り上げます。
ライフゲームとは以下のような生物シミュレーションです。
今回作成したビデオモジュールはキャラクタタイプの表示装置であるので,表示したい箇所のビデオメモリに表示したい文字のキャラクタコードを書き込めばよい.
ビデオモジュールのビデオメモリの先頭アドレスは"xparameters.h"ヘッダファ イル内において,"XPAR_CGA_0_S00_AXI_BASEADDR" に定義されている.これを "VRAM_BASEADDR" とするとき、書き込みたい箇所の座標をX,Yとすると,
(char *)VRAM_BASEADDR + Y * 80 + X 
のアドレスに書き込む.


プログラミング言語CによるLIFEゲームプログラムは以下のようになります。


ソース コメント
#include <xparameters.h>

#define VRAM_BASEADDR (char *)XPAR_CGA_0_S00_AXI_BASEADDR
#define XSIZ 60
#define YSIZ 39

char what( int , int );
void v_puts( int , int , char * );
void v_putc( int , int , char );

static char data1[XSIZ][YSIZ] = {
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  { 0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
};

static char data2[XSIZ][YSIZ];

int
main()
{

  int  i,j,num;
  static char *banner = "Microblaze Softcore Processor Video System";

  v_puts( 0, 0, banner );

  /* Character table */
  for( j=0; j<16; j++ ){
    for( i=0; i<16 ; i++ ) {
      v_putc( i+64, j+9, 16*j+i );
    }
  }

  /* Right side wall */
  for( i=0 ; i<44 ; i++ ) {
    v_putc( 79, i, i%10 + '0' );
  }

  /* Bottom wall */
  for( i=0 ; i<80 ; i++ ) {
    v_putc( i, 44, i%10 + '0' );
  }

  /* Clear DATA2 array */
  for( i=0; i<XSIZ; i++ ){
    for( j=0 ; j<YSIZ ; j++ ) {
      data2[i][j] = 0;
    }
  }

  while(1) {
    for( i=0; i<XSIZ; i++ ){
      for( j=0 ; j<YSIZ ; j++ ) {
        num=0;
        if( what(i-1,j-1) ) num++;
        if( what(i-1,j  ) ) num++;
        if( what(i-1,j+1) ) num++;
        if( what(i  ,j-1) ) num++;
        if( what(i  ,j+1) ) num++;
        if( what(i+1,j-1) ) num++;
        if( what(i+1,j  ) ) num++;
        if( what(i+1,j+1) ) num++;
        if( what(i,j) ) {
          if( num == 2 || num == 3 )
            data2[i][j]=1;
          else
            data2[i][j]=0;
        } else if( num == 3 ) data2[i][j]=1;
      }
    }

    for( i=0; i<XSIZ; i++ ){
      for( j=0 ; j<YSIZ ; j++ ) {
        data1[i][j] = data2[i][j];
        v_putc( XSIZ-i-1, j+5, data2[i][j] ? '@' : ' ' );
        data2[i][j] = 0;
      }
    }

    /* Busy wait */
    for( i=0; i<256; i++ ){
      for( j=0 ; j<1000 ; j++ ) {
        v_putc( 79, 0, i );
      }
    }

  }
}

char what( int i, int j)
{
  if( i < 0 ) return 0;
  if( j < 0 ) return 0;
  if( i == XSIZ ) return 0;
  if( j == YSIZ ) return 0;
  return data1[i][j];
}

void v_puts( int x, int y, char *str )
{
  while( *str != '\0' ) {
    v_putc( x++, y, *str++ );
  }
}

void v_putc( int x, int y, char ch )
{
  volatile char *addr;

  addr = VRAM_BASEADDR + (y*80 + x);
  *addr = ch;
}









data1[XSIZ][YSIZ] : 生物の初期データ
生物が居るところを 1, 居ない所を 0 としている。
初期データとして、ペンタデカスロン (pentadecathlon) および
グライダーガン (gliderguns) が配置されてある。



























































タイトル表示表示 画面上にすべての文字セットを表示する 右に行番号を配置 下に桁番号を配置 data2 配列の初期化 data1 配列の各要素毎に、周りの8マスの生物の状態を調べて生物の
個数をカウ ントする。中心に生物がいるとき、まわりに2〜3つの
生物が居れば、中心の生物は生き残る。
中心に生物がいないとき、まわりに3つの生物が居れば、
中心に生物が誕生する。次世代の状態を data2 配列に作成する。 data2 配列内を data1 配列にコピーするとともに、
画面上に生物の状態を配置する。 256×1000回の時間待ちループ。右上にキャラクタを表示しながらループする。 what( i, j ) : data1 配列内の data1[i][j] の生物の状態を返す関数 v_puts( x, y, string ) : (x,y)座標から string 文字列を画面に表示する。 v_putc( x, y, ch ) : (x,y)座標に ch 文字列を表示する。

このプログラムを,ソフトウェアプロジェクトとして作成します。

Microbladeのプログラムは Vitis IDE というソフトウェア開発ツールを使用します。
Vivadoのメニューから"File" -> "Export" -> "Export Hardware..."を実行します.
最初に立ち上がるウィンドウはそのまま"Next>"を押します.
次に立ち上がるウィンドウにおいて"include bitstream"のチェックを入れて"Next>"を押します.
次に立ち上がるウィンドウにおいて"Hardware Platform File (XSA)"を保存するディレクトリを指定します.
デフォルトのディレクトリは,Vivadoで作成したプロジェクトディレクトリと同じになっているので,そのまま"Next>"を押して大丈夫です.
最後に"Finish"を押します.ここまでの作業で,Vivadoのプロジェクトが作成されているディレクトリ内に, "design_1_wrapper.xsa"というハードウェア情報ファイルが作成されます.

ここからは,ソフトウェア開発環境であるVitis IDEを使います.
Vitisの"Tools"メニューから"Launch Vitis IDE"を実行します.

※ Vitis/Vivado 2023.2 の場合,この操作で新しいVitisが起動するため,以降の操作が大きく異なります.
以降の説明と同じ操作で設計を進めるのであれば,"Vitis Classic" を起動してください.

最初のウィンドウでは,以下のようにVitisのワークスペースディレクトリを設定します.
これは,Vivadoが作成したプロジェクト(life)ディレクトリの中に作成すると良いでしょう.
"Launch"を押して,Vits IDEを起動します.




これにより,以下のようなVitsウィンドウが起動します。



次に、プラットフォームプロジェクトを作成します。メニューの"File"->"New"->"Platform Project..." を選択します。
最初のウィンドウである"Create new Platform project"では,"Project name:"を"life_platform"にして"Next"を押します.




次のウィンドウである"Platform Project"では"Create from hardware specification (XSA)"が選択されていることを確認して,"Next"を押します.
次のウィンドウである"Platform Project Specificcation"では,"XSA file:"の所にVivadoで作成した(.xsa)ファイルを指定します.
指定したXSAファイルにより,"Operating system:"が"Standalone","Processor:"が"Microblaze_0"に変化したことを確認して,"Finish"を押します.




Vieisのウィンドウに戻り,"File"メニューのすぐ下にある"Perspective"アイコンをクリックすると,以下のような"Design perspective"ウィンドウが現れます.





"Explorer"サブウィンドウの"platform.spr"をマウスでダブルクリックし,"life_platform"サブウィンドウの下にある"Hardware Specification"タブに切り替えると,Vivadoで作成したハードウェア情報を参照することができます.



"Explorer"サブウィンドウの"lifew_platform"をマウスでクリックし,マウスの右メニューから"Build Project"を選択し実行します.これにより,Vivadoで作成したハードウェアを制御するためのミドルウェアがコンパイルされます.



"Console"サブウィンドウに"Build Finished"が表示されて正常終了して入ればOKです.




重要

Vitis 2020.2 では,Pratform Project の一部でコンパイルが正常に終了しないことが分かっています.
以下のように,2箇所のMakefileを修正して,再度Buildします.
  1. Explorerウィンドウ内で,
    life_platform/hw/drivers/cga_v1_0/src/Makefile および
    life_platform/microblaze_0/standalone_domain/bsp/microblaze_0/libsrc/cga_v1_0/src/Makefile
    ファイル内の
          libs:
              echo "Compiling cga..."
              $(COMPILER) $(COMPILER_FLAGS) $(EXTRA_COMPILER_FLAGS) $(INCLUDES) $(LIBSOURCES)
              $(ARCHIVER) -r ${RELEASEDIR}/${LIB} ${OUTS}
              make clean
        
    を以下のように3行コメントにする
          libs:
              echo "Compiling cga..."
          #   $(COMPILER) $(COMPILER_FLAGS) $(EXTRA_COMPILER_FLAGS) $(INCLUDES) $(LIBSOURCES)
          #   $(ARCHIVER) -r ${RELEASEDIR}/${LIB} ${OUTS}
          #   make clean
        
Makefileを保存した後,もう一度Buildを行います.
次は,アプリケーションプロジェクトを作成します.メニューの"File"->"New"->"Application Project..." を選択します.
最初のウィンドウである"Create new Platform project"では,"Project name:"を"life_sw"にして"Next"を押します.




次のウィンドウである"Platform"では"Hardware Platform"として"life_platform"(Boardはnexys-a7-100tとなっている)が選択されていることを確認して,"Next"を押します.
次のウィンドウである"Domain"は"Domain:"が"standalone on microblaze_0", "Language:"が"C"になっていることを確認して,"Next"を押します.
次のウィンドウである"Templates"は"Enpty Application"を選択して,"Finish"を押します.
以下のように,"Explorer"サブウィンドウに"life_sw_system"が作成できていればOKです.



次に"Explorer"サブウィンドウ内の"life_sw_system"の"src"所でマウスの右をクリックし、"New"->"File" をマウスの左で選択します。



"File" ウィンドウの "File name:" の所に "life.c" と入力して "Finish" を押します。



これにより,"life.c" のテキストエディタが起動します.
ここに,上に記載したLifeゲームのプログラムを入力し保存します。



"Explorer"サブウィンドウの"life_sw_system"をマウスでクリックし,マウスの右メニューから"Build Project"を選択し実行します.



これにより,"life.c"がコンパイルされVivadoで作成したハードウェアを制御するためのミドルウェアがコンパイルされ,Microblazeプロセッサで実行できるバイナリ"life_sw.elf"が生成されます.




それでは実際に、 動作確認 を行ってみます。
| Back | Top | Next |