FC2カウンター FPGAの部屋 Vivado HLS 2014.4 でディスプレイ・コントローラを作る1(高位合成、C/RTLコシミュレーション)

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

FPGAの部屋

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

Vivado HLS 2014.4 でディスプレイ・コントローラを作る1(高位合成、C/RTLコシミュレーション)

まだ、AXI4-Stream 版ラプラシアンフィルタIP の実装の途中なのだが、Vivdo HLS 2014.4 でディスプレイ・コントローラを作り始めてしまったので、こっちを先に書くことにする。

まずは、C++ のソースコードを示す。ちょっと理由があって display_cont() とdisplay_cont_sub() の2つの関数を書いた。
display_cont() はこう書くんじゃないかな?という自分の予想で、1フレームの水平、垂直カウンタのカウントを while(1){ } で囲うことによって無限ループにしている。しかしこれではC シミュレーションも C/RTL コシミュレーションもできないし、レイテンシやインターバルの値も?になってしまう。
もしかすると、display_cont_sub() でも、”#pragma HLS INTERFACE ap_ctrl_none port=return”だと、制御用のポートが全てなくなって、最初の初期化過程は踏むかもしれないが、ループするのでは?という疑惑もある。これは後で、display_cont() とdisplay_cont_sub() の高位合成で生成されたVerilog HDL を自分でテストベンチを書いてシミュレーションしてみたい。なお、現在はC/RTL コシミュレーションのために ap_ctrl_hs に指定してある。

// display_cont.cpp
// 2015/06/03 by marsee
//
// 画面を4分割した第1象限は赤、第2象限は緑、第3象限は青、第4象限は白を表示する
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

// SVGA 解像度
#define H_ACTIVE_VIDEO    800
#define H_FRONT_PORCH    40
#define H_SYNC_PULSE    128
#define H_BACK_PORCH    88
#define H_SUM            (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH)

#define V_ACTIVE_VIDEO    600
#define V_FRONT_PORCH    1
#define V_SYNC_PULSE    4
#define V_BACK_PORCH    23
#define V_SUM            (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH)

void display_cont(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx){
#pragma HLS INTERFACE ap_none register port=red
#pragma HLS INTERFACE ap_none register port=green
#pragma HLS INTERFACE ap_none register port=blue
#pragma HLS INTERFACE ap_none register port=display_enable
#pragma HLS INTERFACE ap_none register port=hsyncx
#pragma HLS INTERFACE ap_none register port=vsyncx
#pragma HLS INTERFACE ap_ctrl_none port=return

    ap_uint<16> h_count, v_count;

    while(1){
        for (v_count=0; v_count<V_SUM; v_count++){
            for (h_count=0; h_count<H_SUM; h_count++){
#pragma HLS PIPELINE
#pragma HLS LATENCY min=1 max=1
                if (h_count > (H_ACTIVE_VIDEO +H_FRONT_PORCH) && h_count < (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE))
                    *hsyncx = 0;
                else
                    *hsyncx = 1;

                if (v_count > (V_ACTIVE_VIDEO + V_FRONT_PORCH) && v_count < (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE))
                    *vsyncx = 0;
                else
                    *vsyncx = 1;

                if (h_count < H_ACTIVE_VIDEO && v_count < V_ACTIVE_VIDEO)
                    *display_enable = 1;
                else
                    *display_enable = 0;

                if (v_count < V_ACTIVE_VIDEO/2){
                    if (h_count < H_ACTIVE_VIDEO/2){
                        *red=0xff; *green=0; *blue=0;
                    } else if (h_count < H_ACTIVE_VIDEO){
                        *red=0; *green=0xff; *blue=0;
                    } else {
                        *red=0; *green=0; *blue=0;
                    }
                } else if (v_count < V_ACTIVE_VIDEO){
                    if (h_count < H_ACTIVE_VIDEO/2){
                        *red=0; *green=0; *blue=0xff;
                    } else if (h_count < H_ACTIVE_VIDEO){
                        *red=0xff; *green=0xff; *blue=0xff;
                    } else {
                        *red=0; *green=0; *blue=0;
                    }
                } else {
                    *red=0; *green=0; *blue=0;
                }
            }
        }
    }

}

void display_cont_sub(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx){
#pragma HLS INTERFACE ap_none register port=red
#pragma HLS INTERFACE ap_none register port=green
#pragma HLS INTERFACE ap_none register port=blue
#pragma HLS INTERFACE ap_none register port=display_enable
#pragma HLS INTERFACE ap_none register port=hsyncx
#pragma HLS INTERFACE ap_none register port=vsyncx
#pragma HLS INTERFACE ap_ctrl_hs port=return

    ap_uint<16> h_count, v_count;

    for (v_count=0; v_count<V_SUM; v_count++){
        for (h_count=0; h_count<H_SUM; h_count++){
#pragma HLS PIPELINE
#pragma HLS LATENCY min=1 max=1
            if (h_count > (H_ACTIVE_VIDEO +H_FRONT_PORCH) && h_count < (H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE))
                *hsyncx = 0;
            else
                *hsyncx = 1;

            if (v_count > (V_ACTIVE_VIDEO + V_FRONT_PORCH) && v_count < (V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE))
                *vsyncx = 0;
            else
                *vsyncx = 1;

            if (h_count < H_ACTIVE_VIDEO && v_count < V_ACTIVE_VIDEO)
                *display_enable = 1;
            else
                *display_enable = 0;

            if (v_count < V_ACTIVE_VIDEO/2){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0xff; *green=0; *blue=0;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0; *green=0xff; *blue=0;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else if (v_count < V_ACTIVE_VIDEO){
                if (h_count < H_ACTIVE_VIDEO/2){
                    *red=0; *green=0; *blue=0xff;
                } else if (h_count < H_ACTIVE_VIDEO){
                    *red=0xff; *green=0xff; *blue=0xff;
                } else {
                    *red=0; *green=0; *blue=0;
                }
            } else {
                *red=0; *green=0; *blue=0;
            }
        }
    }
}


C コードを見れば何を表示するものかすぐわかると思うが、画面を4つに分けて、左上には赤を、右上には緑、左下には青、右下には白の四角を表示する回路だ。
いろいろとディレクティブを試行錯誤したのだが、行の for() 文に PIPELINE ディレクティブを付ければ良いようだ。なお、LATENCY = 1 にすると、行のループに必要なクロックが1クロック減る効果があった。
この display_cont_sub() を Top Function に指定して、高位合成を行った。結果を下に示す。なお、画面の解像度は800 x 600 ピクセルなので、標準的なピクセルクロックの 40 MHz = 25 ns に設定した。
HLS_Disp_Cont_1_150605.png

HLS_Disp_Cont_2_150605.png

Analyse 結果を示す。PIPELINEディレクティブを指定し、行の for() ループに信号の決定を集めたので、1クロックで動作が完了している。
HLS_Disp_Cont_3_150605.png

C/RTL コシミュレーションを行った。その際に、Dump Trace を all に設定して、C/RTL コシミュレーション後の波形を見られるようにした。
HLS_Disp_Cont_4_150605.png

C/RTL コシミュレーションの結果を示す。
HLS_Disp_Cont_5_150605.png

水平ピクセル x 垂直ラインの総ピクセル数は、(800+40+128+88)*(600+1+4+23) = 663168 ピクセルだ。
C/RTL コシミュレーションの結果のレイテンシは 663170 で2クロック違うだけだ。

Vivado 2015.1 を立ちあげてC/RTL コシミュレーションを観察した。
その際に使用したTCLコマンドを下に示す。

cd C:/Users/Masaaki/Documents/Vivado_HLS/ZYBO/display_cont/solution1/sim/verilog
current_fileset
open_wave_database display_cont_sub.wdb
open_wave_config display_cont_sub.wcfg


C/RTL コシミュレーションの波形を下に示す。
HLS_Disp_Cont_6_150605.png

前半部分を拡大した。
HLS_Disp_Cont_7_150605.png

水平同期信号の1週期は 26.4 us だった。それをピクセルクロックの 25 ns で割ると、26400 ns / 25 ns = 1056 クロックとなった。これは水平ラインの総ピクセル数 800+40+128+88 = 1056 と合っている。

最後にテストベンチの display_cont_tb.cpp を示す。

//
// display_cont_tb.cpp
// 2015/06/03 by marsee
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

void display_cont_sub(ap_uint<8> *red, ap_uint<8> *green, ap_uint<8> *blue, ap_uint<1> *display_enable, ap_uint<1> *hsyncx, ap_uint<1> *vsyncx);
int main(){
    ap_uint<8> redb, *red;
    ap_uint<8> greenb, *green;
    ap_uint<8> blueb, *blue;
    ap_uint<1> deb, *display_enable;
    ap_uint<1> hb, *hsyncx;
    ap_uint<1> vb, *vsyncx;

    red = &redb;
    green = &greenb;
    blue = &blueb;
    display_enable = &deb;
    hsyncx = &hb;
    vsyncx = &vb;

    display_cont_sub(red, green, blue, display_enable, hsyncx, vsyncx);

    return 0;
}

  1. 2015年06月05日 05:41 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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