FC2カウンター FPGAの部屋 ZedBoardにビットマップ・ディスプレイ・コントローラを追加する16(ARMのソフトからキャラクタを書けた)
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する16(ARMのソフトからキャラクタを書けた)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する15(ChipScope での評価)”の続き。

実は、アナデバのリファレンス・デザインを見ていたが、HDMIの出力画像はARMのソフトウェアから書いてあった。それだと、”ZedBoardにビットマップ・ディスプレイ・コントローラを追加する6(完成とは言えない)”の様にキャッシュ?のために表示がおかしくなるはずなのに正常に表示されている。その違いはどこなのか、ソフトウェアを調べてみた。

そうすると、メモリに書いた後に、Xil_DCacheFlush(); を実行していました。これだ~~~と思いましたね。。。

そこで早速、自分のコードに、Xil_DCacheFlush(); を追加した。
最初に、DDR3 SDRAMの画像フレームバッファをクリアしてから、Xil_DCacheFlush(); を実行すると、画面がすべて真っ黒になった。やはり、Dキャッシュからcast out しなければダメなのか。。。

1.1ドット描画する部分に、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのがとっても遅かった。約68秒掛かった。(drawn_disp.c の // 1番目の時間計測 を参照)

2.1キャラクタ描画してから、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのに、約2秒掛かった。(drawn_disp.c の // 2番目の時間計測 を参照)

3.全部のキャラクタを書き終えてから、Xil_DCacheFlush(); を入れたらキャラクタをVGA画面分描画するのに、1秒以下位だった。手計測なので、良く計測できない。(drawn_disp.c の // 3番目の時間計測 を参照)(:上の2つと違って、キャラクタをWriteするCコードを実行する時間が基本的に入っていないので、値が正しくない可能性がある。但し、Cコード実行の途中でキャッシュが溢れて一部のデータはメモリに書かれているかもしれない?)

これらの結果からXil_DCacheFlush(); で経過した時間を計算することにする。先ずは、Xil_DCacheFlush(); の呼び出しと実行にかかる時間を y とする。それ以外の時間を x とする。x は、本来のビットマップ・ディスプレイ上にキャラクタをWriteする時間となる。
1.で得られたキャラクタWriteの経過時間 68sec で式を立てる。ここでは、Xil_DCacheFlush(); をピクセル1ドット毎に行なっているので、Xil_DCacheFlush(); を行う回数は 640 x 480 回となる。よって、(1) 式が導ける。

x + 640 x 480 x y = 68sec ----- (1)


次に、2.で得られたキャラクタWriteの経過時間 2sec で式を立てる。ここでは、Xil_DCacheFlush(); を1文字ごとに行なっている。キャラクタの総数は 80 x 60 文字なので、Xil_DCacheFlush(); を呼び出す回数は 80 x 60 回となる。よって、(2) 式が導ける。

x + 80 x 60 x y = 2 ----- (2)


これで2つの式が出来たので、x と y について解くことが出来る。先ずは、y から求めてみる。
式 (1) から式 (2) を引くと x が消去できる。

(640 x 480 - 80 x 60) x y = 68 - 2
y = 21 x 10の-6乗 = 21 usec


Xil_DCacheFlush(); の実行時間は 21usec となった。これはかなり遅いということが出来ると思う。ARM Cortex - A9のIPC (Instructions Per Clock) を知りませんが、仮に1と仮定すると、クロック周波数は667MHz ですから、1命令の実行時間は約1.5nsec となる。21usec では、21usec / 1.5 nsec = 14,000命令実行できることになる。これはあくまでもCPUの動作周波数で実行できる命令数なので、1st cache に入っている命令だけだが、かなり多くの命令を実行しているということがわかると思う。
次に、x を求めてみる。今度は y を消去するために、式(1) から、8 x 8倍した式(2) を引くことにする。そうすると下の式が導ける。

-63x = -60
x = 60/63 = 0.95 sec


よって、Xil_DCacheFlush(); の実行時間を除いたキャラクタWriteの実行時間は、0.95sec となった。これは3.での結果と合致すると言うことが出来る。最後にキャッシュをメモリに吐き出すので、1回のみXil_DCacheFlush(); を実行する事になり、Xil_DCacheFlush(); の実行コストが最小になる。

#計算が間違っていたら、お知らせください。

下に、キャラクタのWriteが終了した時点の写真を示す。
ZedBoad_BitMap_DispCnt_100_121101.jpg

drawn_disp.c のCコードを下に示す。

/*
 * drawn_disp.c
 *
 *  Created on: 2012/09/25
 *      Author: Masaaki
 */

#include "xparameters.h"
#include "xgpio.h"

#define VIDEO_BUFFER_START_ADDRESS    0x10000000

void Xil_DCacheFlush(void);

// char_draw: アドレスとキャラクタコードを受け取ってそのキャラクタを描画する
// addr : 描画するキャラクタのスタートアドレス
// char_code : 描画するキャラクタのコード
// char_color : 描画するキャラクタのカラー、32ビットで表される、0RGBと8ビットずつで表す
// 戻り値:次のキャラクタの描画先頭アドレスを示す。
unsigned int *char_draw(unsigned int *addr, unsigned char char_code, unsigned int char_color){
    int i,j;
    unsigned int char_pattern;
    unsigned int char_code_int;
    unsigned int *cal_char_addr;
    unsigned int *return_addr;

    char_code_int = (unsigned int)(char_code);
    return_addr = addr + 8;
    cal_char_addr = (unsigned int *)(XPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDR+((char_code_int<<3)<<2)); // キャラジェネROMのデータは32ビット幅で下の8ビットだけ有効
    for(i=0; i<8; i++){
        char_pattern = *(volatile unsigned int *)(cal_char_addr); // キャラクタのパターンを読み出し
        for(j=0; j<8; j++){
            if(char_pattern & 0x1) // 7ビット目が1の時はドットを描画
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = char_color;
            else
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = 0; // 黒を描画
//            Xil_DCacheFlush();    // 1番目の時間計測
            addr++;
            char_pattern >>= 1; // キャラクタのパターンを1ビット右シフト
        }
        addr -= 8; // 行の最初のアドレスに戻す
        addr += 640; // アドレスを1行下にする
        cal_char_addr++;
    }
//    Xil_DCacheFlush();    // 2番目の時間計測

    return return_addr;
}

int main()
{
    static XGpio GPIOInstance_Ptr;
    int xStatus;
    unsigned char char_code;
    unsigned int * ddr2_addr;
    unsigned int coler_code;
    unsigned int char_cnt;
    int i, j;

    // AXI GPIO Initialization
    xStatus = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
    if(XST_SUCCESS != xStatus)
        print("GPIO INIT FAILED\n\r");
    // AXI GPIO Set the Direction(Output setting)
    XGpio_SetDataDirection(&GPIOInstance_Ptr, 1, 0);
    // init_doneに1を出力
    XGpio_DiscreteWrite(&GPIOInstance_Ptr, 1, 1);

    for (ddr2_addr=(unsigned int *)VIDEO_BUFFER_START_ADDRESS; ddr2_addr<(unsigned int *)((unsigned int)VIDEO_BUFFER_START_ADDRESS + (unsigned int)0x12C000); ddr2_addr++){
        *(volatile unsigned int *)((unsigned int)ddr2_addr ^ 4) = 0;
    }
    Xil_DCacheFlush();

    ddr2_addr = (unsigned int *)VIDEO_BUFFER_START_ADDRESS;
    char_cnt = 0;
    for(j=0; j<8; j++){
        for(i=1; i<8; i++){
            switch(i){
                case 1 :
                    coler_code = 0xff;
                    break;
                case 2 :
                    coler_code = 0xff00;
                    break;
                case 3 :
                    coler_code = 0xffff;
                    break;
                case 4 :
                    coler_code = 0xff0000;
                    break;
                case 5 :
                    coler_code = 0xff00ff;
                    break;
                case 6 :
                    coler_code = 0xffff00;
                    break;
                case 7 :
                    coler_code = 0xffffff;
            }

            for(char_code=0x21; char_code<0x80; char_code++){
                if(char_code >= 0x80) // キャラクタコードが上限に達したら、一番下に戻す
                    char_code = 0x21;
                if (char_cnt!=0 && char_cnt%80==0)
                    ddr2_addr = (unsigned int *)((unsigned int)ddr2_addr + 640*4*7); // 1行書き終わったので、下の行に、アドレスは640ドットx1ピクセル4バイトx7行
                ddr2_addr = char_draw(ddr2_addr, char_code, coler_code); // キャラクタを描画
                char_cnt++;
            }
        }
    }

    Xil_DCacheFlush();    // 3番めの時間計測
    return 0;
}

  1. 2012年10月31日 05:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/2294-cc203110
この記事にトラックバックする(FC2ブログユーザー)