FC2カウンター FPGAの部屋 ZynqBerryZero
fc2ブログ

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

FPGAの部屋

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

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する7(再度、実機確認)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する6(Vitis HLS で青と緑を反転)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、青と緑のデータを反転するために、AXI4-Stream インターフェースで青と緑を反転する IP を Vitis HLS 2020.2 で作成した。今回は、その青と緑のデータを反転するIP を Vivado のブロックデザインに入れて論理合成、インプリメンテーション、ビットストリームの生成を行って、実機確認する。

reverse_rgb IP をブロックデザインの AXI4-Stream インターフェース部分に入れた。
ZynqBerryZero_172_201217.png

これで、論理合成、インプリメンテーション、ビットストリームの生成を行った。結果を示す。
ZynqBerryZero_173_201217.png

ハードウェアをエクスポートし、 lap_filter_axis_bd_wrapper.xsa を更新した。

Vitis で、 Explorer ウインドウの lap_filter_axis_bd_wrapper プラットフォームを右クリックし、右クリックメニューから Update Hardwear Specification を選択した。

Explorer ウインドウの lap_filter_axis_test_system を右クリックし右クリックメニューから Clean Project を選択した。

Explorer ウインドウの lap_filter_axis_test_system を右クリックし右クリックメニューから Build Project を選択して、ビルドした。
ZynqBerryZero_174_201217.png

Assistant ウインドウの lap_filter_axis_test_system -> lap_filter_axis_test -> Debug を右クリックし、右クリックメニューから Run -> Debugger_lap_filter_axis_test-Default (Single Application Debug) を選択した。

ZynqBerryZero がコンフィギュレーションされて、アプリケーション・ソフトウェアが実行された。

画像が表示された。
ZynqBerryZero_176_201217.jpg

やった〜。。。

起動しておいた gtkterm で 1 を押すと、ラプラシアン・フィルタ処理された画像が表示された。
ZynqBerryZero_177_201217.jpg

しかし左端が水色になっている。これはなぜ?
よく見ると元画像にも水色の線がある。
これの原因はまだ分からないが、とりあえず、元画像とラプラシアン・フィルタ処理後の画像を表示することができた。

最後に gtkterm の画面を示す。
ZynqBerryZero_175_201217.png
  1. 2020年12月18日 03:50 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する6(Vitis HLS で青と緑を反転)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する5(デバック 編)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、 実機確認を行ったところ、画像が真っ赤だった原因を追求するということで、カラーパターンを表示させたところ、青と緑が反転しているということが分かった。今回は、青と緑を反転するために、AXI4-Stream インターフェースで青と緑を反転する IP を Vitis HLS 2020.2 で作成する。

Vitis HLS 2020.2 で赤、緑、青を反転することができる汎用 IP を作成する。
AXI4 Lite インターフェースでレジスタを設定して、反転する色を決定するのでもよいのだが、今回は、色を決め打ちで define 指定して、 IP の起動も自動で起動するようにブロック・レベルのインターフェースに ap_ctrl_none を使用する。

Vitis HLS 2020.2 で reverse_rgb プロジェクトを作成した。
プロジェクトを作成する時に、 Part として、 xc7z010clg225-1 を指定した。
ZynqBerryZero_160_201216.png

C シミュレーションを行った。
青と緑だけビット反転して、赤はそのままということが分かる。
ZynqBerryZero_161_201216.png

ZynqBerryZero_162_201216.png

C コードの合成を行った。
ZynqBerryZero_163_201217.png
ZynqBerryZero_164_201217.png

C/RTL 協調シミュレーションを行った。
ZynqBerryZero_165_201217.png

480010 クロックと性能は良いようだ。

C/RTL 協調シミュレーションの波形を示す。
ZynqBerryZero_166_201217.png

outs_TVALID や ins_TREADY もほとんど 1 でスループットが最大になっているのが分かる。

次に、ブロック・レベルのインターフェースを ap_ctrl_hs から ap_ctrl_none に変更した。
なぜ、 ap_ctrl_hs でやっていたかと言うと、 ap_ctrl_none では、 C/RTL 協調シミュレーションが実行できないからだ。
再度、 C コードの合成をを行った。
ZynqBerryZero_168_201217.png
ZynqBerryZero_169_201217.png

ap_ctrl が ap_ctrl_none になっただけで、ほとんど変更は無い。

Export RTL を行った。
ZynqBerryZero_170_201217.png

問題無さそうだ。

solution1/impl には export.zip, solution1/impl/ip には、 xilinx_com_hls_reverse_rgb_1_0.zip が生成されていた。
ZynqBerryZero_171_201217.png

reverse_rgb.h を貼っておく。

// reverse_rgb.h
// 2020/12/16 by marsee
//

#ifndef __REVERSE_RGB_H__
#define __REVERSE_RGB_H__

#define HORIZONTAL_PIXEL_WIDTH 800
#define VERTICAL_PIXEL_WIDTH 600

#define REVERSE_RED  false
#define REVERSE_GREEN true
#define REVERSE_BLUE  true

#endif


reverse_rgb.cpp を貼っておく。

// reverse_rgb.cpp
// 2020/12/16 by marsee
// data format: 8'd0, Red[8:0], Green[8], Blue[8]
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include "reverse_rgb.h"

int reverse_rgb(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis register_mode=both register port=outs
#pragma HLS INTERFACE axis register_mode=both register port=ins
//#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_ctrl_none port=return
    ap_axis<32,1,1,1> pix, rev_pix;

    LOOP_WAIT: do{ // wait if user signal = 1
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y: for(int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        LOOP_X: for(int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // The first input has already been entered
                ins >> pix; // Input from AXI4-Stream

            if(REVERSE_RED == true)
                rev_pix.data = (~pix.data) & 0xff0000;
            else
                rev_pix.data = pix.data & 0xff0000;
            if(REVERSE_GREEN == true)
                rev_pix.data |= (~pix.data) & 0xff00;
            else
                rev_pix.data |= pix.data & 0xff00;
            if(REVERSE_BLUE == true)
                rev_pix.data |= (~pix.data) & 0xff;
            else
                rev_pix.data |= pix.data & 0xff;

            rev_pix.user = pix.user;
            rev_pix.last = pix.last;
            outs << rev_pix;
        }
    }
    return(0);
}


reverse_rgb_tb.cpp を貼っておく。

// reverse_rgb_tb.cpp
// 2020/12/16 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>
#include "reverse_rgb.h"

int reverse_rgb(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);
int reverse_rgb_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);

int main(){
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs;
    hls::stream<ap_axis<32,1,1,1> > outs_soft;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;
    ap_axis<32,1,1,1> vals_soft;
    int xy;

    // Prepare input data in ins and ins_soft
    for(int i=0; i<5; i++){ // dummy data
        pix.user = 0;
        pix.last = 0;
        pix.data = i;
        ins << pix;
        ins_soft << pix;
    }
    for(int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for(int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            xy = x*(y+1);
            pix.data = xy & 0xffffff;

            if(y==0 && x==0)
                pix.user = 1;
            else
                pix.user = 0;

            if(x == HORIZONTAL_PIXEL_WIDTH-1)
                pix.last = 1;
            else
                pix.last = 0;

            ins << pix;
            ins_soft << pix;
        }
    }

    reverse_rgb(ins, outs);
    reverse_rgb_soft(ins_soft, outs_soft);

    // Checking hardware and software values
    cout << endl;
    cout << "outs" << endl;
    for(int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for(int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_int<32> val = vals.data;
            ap_int<32> val_soft = vals_soft.data;
            if(val != val_soft){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n", x, y, (int)val, (int)val_soft);
                return(1);
            }
            printf("x = %ld, y = %ld, xy = %x, HW = %x, SW = %x\n", x, y, (x*(y+1)&0xffffff), (int)val, (int)val_soft);
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    return(0);
}

int reverse_rgb_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
    ap_axis<32,1,1,1> pix, rev_pix;

    do{ // wait if user signal = 1
        ins >> pix;
    } while(pix.user == 0);

    for(int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for(int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (!(x==0 && y==0))    // The first input has already been entered
                ins >> pix; // Input from AXI4-Stream

            if(REVERSE_RED == true)
                rev_pix.data = (~pix.data) & 0xff0000;
            else
                rev_pix.data = pix.data & 0xff0000;
            if(REVERSE_GREEN == true)
                rev_pix.data |= (~pix.data) & 0xff00;
            else
                rev_pix.data |= pix.data & 0xff00;
            if(REVERSE_BLUE == true)
                rev_pix.data |= (~pix.data) & 0xff;
            else
                rev_pix.data |= pix.data & 0xff;

            rev_pix.user = pix.user;
            rev_pix.last = pix.last;
            outs << rev_pix;
        }
    }
    return(0);
}

  1. 2020年12月17日 05:00 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する5(デバック 編)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する4(Vitis 2020.2 編)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、 Vivado 2020.2 で Export Hardware を行って、Vitis 2020.2 を立ち上げ、プラットフォームとアプリケーション・プロジェクトを作成して、実機確認を行ったところ、画像が真っ赤だった。今回は、その原因を追求する。

なぜ画像が真っ赤だったか?を追求してみよう。赤が出ているというわけじゃなくて、何処かの色が赤につながっているのかも知れない?
それを確認するためにカラーパターンの画像を用意した。
ZynqBerryZero_156_201215.png

これを、bmp_header_file で C のヘッダファイルに変換する。
ZynqBerryZero_157_201215.png

C シミュレーションで変換することができた。
ZynqBerryZero_158_201215.png

できあがった bmp_data.h を Vitis の bmp_data.h と交換してビルドした。
ZynqBerryZero_159_201215.png

これで、実機確認してみた結果を示す。
ZynqBerryZero_159_201216.jpg

赤が白、緑が青、青が緑、白が赤になっている。
この結果から導き出せる原因は、赤は正常、青と緑は逆の出力になっているということだ。
つまり青と緑は255で色がでるのではなく、色が無くなる方向に行っているのではないか?
赤は正常で255だと色が出ているように見える。

なお、ゆうさんからツィッターで Red はHDMI のデータ 2 だったというのを教えてもらった。ありがとうございます。
フリー百科事典『ウィキペディア(Wikipedia)』の”Digital Visual Interface”を見ても、
データ0 が Blue, データ 1 が Green, データ 2 が Red のようだ。

そうなると、ZynqBerryZero の回路図
ZynqBerryZero_131_201213.png

HDMI_TX2 の Red 以外の P と N が反転していたということと辻褄が合う。そうか、論理を反転する必要があったのか?

この結果からAXI4 Streamの途中に青と緑を反転するAXI4 Stream IP を付けることにしよう。
  1. 2020年12月16日 04:50 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する4(Vitis 2020.2 編)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する3(Vivado 2020.2編1)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、 IP を 800 ピクセル X 600 行に変更し(現在は、 64 ピクセル X 48 行)、 C コードの合成、 Export RTL を行って IP を修正してから、その IP を Vivado 2020.2 で使用して、回路を作成し、論理合成、インプリメンテーション、ビットストリームの生成を行った。今回は、Export Hardware を行って、Vitis 2020.2 を立ち上げ、プラットフォームとアプリケーション・プロジェクトを作成して、実機確認を行う。

XSA ファイルを作成する。

Vivado 2020.2 で、File メニューから Export -> Export Hardware... を選択する。

Export Hardware Platform ダイアログが立ち上げた。

Output では、Include bitstream のラジオボタンをクリックする。

後はデフォルトのまま lap_filter_axis_bd_wrapper.xsa を作成する。
ZynqBerryZero_137_201213.png

Vitis 2020.2 を起動する。

Vivado の Tools メニューから Launch Vitis IDE を選択する。

Vitis IDE Launcher が起動する。
Brows... ボタンをクリックして、ZynqBerryZero/lap_filter_axis_test ディレクトリの下に vitis_work ディレクトリを新規作成する。
Launch ボタンをクリックする。
ZynqBerryZero_138_201213.png

Vitis 2020.2 が起動した。
Create Application Project をクリックして、アプリケーション・プロジェクトを新規作成する。
ZynqBerryZero_139_201213.png

New Application Project ダイアログが表示された。
ZynqBerryZero_140_201213.png

Platform では、Create a new platform form hardware (XSA) タブをクリックする。
Hardware Specfication の Browse... ボタンをクリックして、 lap_filter_axis_bd_wrapper.xsa を指定した。
ZynqBerryZero_141_201213.png

Application Project Details では、Application project name を lap_filter_axis_test とした。
ZynqBerryZero_142_201213.png

Domain はそのままとした。
ZynqBerryZero_143_201213.png

Templates では、 Empty Application を選択する。
ZynqBerryZero_144_201213.png

プラットフォームとアプリケーション・プロジェクトが新規作成された。
ZynqBerryZero_145_201213.png

bmp_data.h と lap_filter_axis_test.c をインポートした。
ZynqBerryZero_146_201213.png

lap_filter_axis_test.c を示す。

/*
 * lap_filter_axis_test.c
 *
 *  Created on: 2020/12/13
 *      Author: marsee
 */

#include <stdio.h>
#include "xil_io.h"
#include "xparameters.h"

#include "xlap_filter_axis.h"
#include "xdma2axis.h"
#include "xaxis2dma2st.h"
#include "bmp_data.h"

#define FRAME_BUFFER_ADDRESS 0x8000000
#define DMA_DEST_ADDRESS 0x8200000

#define HORIZONTAL_PIXELS   800
#define VERTICAL_LINES      600

int bmp_write(unsigned int addr);
void Xil_DCacheFlush(void);

int main(){
    XLap_filter_axis xlf_axis_ap;
    XDma2axis xdma2axis_ap;
    XAxis2dma2st xaxis2dma_ap;
    int inbyte_in;

    XLap_filter_axis_Initialize(&xlf_axis_ap, 0);
    XDma2axis_Initialize(&xdma2axis_ap, 0);
    XAxis2dma2st_Initialize(&xaxis2dma_ap, 0);

    XDma2axis_Set_y_size(&xdma2axis_ap, (u32)VERTICAL_LINES);
    XDma2axis_Set_x_size(&xdma2axis_ap, (u32)HORIZONTAL_PIXELS);
    XDma2axis_Set_sel(&xdma2axis_ap, (u32)0);
    XDma2axis_Set_in_V(&xdma2axis_ap, (u32)FRAME_BUFFER_ADDRESS);

    XAxis2dma2st_Set_y_size(&xaxis2dma_ap, (u32)VERTICAL_LINES);
    XAxis2dma2st_Set_x_size(&xaxis2dma_ap, (u32)HORIZONTAL_PIXELS);
    XAxis2dma2st_Set_sel(&xaxis2dma_ap, (u32)0);
    XAxis2dma2st_Set_out_V(&xaxis2dma_ap, (u32)DMA_DEST_ADDRESS);

    bmp_write(FRAME_BUFFER_ADDRESS);
    Xil_DCacheFlush();

    XAxis2dma2st_Start(&xaxis2dma_ap);
    XLap_filter_axis_Start(&xlf_axis_ap);
    XDma2axis_Start(&xdma2axis_ap);

    Xil_Out32(XPAR_BITMAP_DISP_CNTRLER_0_BASEADDR, DMA_DEST_ADDRESS);

    while(1){
        printf("\nPlease input <0> or <1> (<q> : exit) = ");
        fflush(stdout);
        inbyte_in = inbyte();
        printf("%c", inbyte_in);
        fflush(stdout);
        switch(inbyte_in) {
            case '0': //bmp image
                XDma2axis_Set_sel(&xdma2axis_ap, (u32)0);
                XAxis2dma2st_Set_sel(&xaxis2dma_ap, (u32)0);

                XAxis2dma2st_Start(&xaxis2dma_ap);
                XLap_filter_axis_Start(&xlf_axis_ap);
                XDma2axis_Start(&xdma2axis_ap);
                break;
            case '1': // Laplacian filter
                XDma2axis_Set_sel(&xdma2axis_ap, (u32)1);
                XAxis2dma2st_Set_sel(&xaxis2dma_ap, (u32)1);

                XAxis2dma2st_Start(&xaxis2dma_ap);
                XLap_filter_axis_Start(&xlf_axis_ap);
                XDma2axis_Start(&xdma2axis_ap);
                break;
            case 'q': // exit
                return(0);
        }
    }
}

int bmp_write(unsigned int addr){
    for(int y=0; y<VERTICAL_LINES; y++){
        for(int x=0; x<HORIZONTAL_PIXELS; x++){
            Xil_Out32(addr+(y*HORIZONTAL_PIXELS+x)*sizeof(int),
                ((int)bmp_file_array[y][x][2]<<16)+((int)bmp_file_array[y][x][1]<<8)+(int)bmp_file_array[y][x][0]);
        }
    }
    return(0);
}


bmp_data.h は”Vivado HLS 2019.1 を使用してBMPファイルをC のヘッダファイルに変換する”を使用して、800 ピクセル X 600 行の画像を C のヘッダファイルに変換してある。

lap_filter_axis_test_system を右クリックし右クリックメニューから Build Project を選択する。
ビルドが始まり、 lap_filter_axis_test.elf が生成された。
ZynqBerryZero_147_201213.png

ZynqBerryZero を USB でパソコンに接続し、ミニHDMI ポートからディスプレイに接続した。

gtkterm を起動して、ZynqBerryZero の USB シリアルポートに接続し、115200 bps に設定した。

Assistant ウインドウの lap_filter_axis_test_system -> lap_filter_axis_test -> Debug を右クリックし右クリックメニューから Run -> Launch on Hardware (Single Application Debug) を選択する。
ZynqBerryZero_148_201213.png

ZynqBerryZero をコンフィギュレーションし、アプリケーション・ソフトウェアを起動した。
絵はディスプレイに表示されたのだが、真っ赤だった。
ZynqBerryZero_156_201214.jpg

gtkterm 上で 1 を入力すると、エッジの画像にはなっているようだ。
ZynqBerryZero_157_201214.jpg

白黒反転しているようでもある?

TMDS_tx_2_G 以外の _P, _N を反転してみた。
ZynqBerryZero_152_201213.png

制約ファイルを作成した。
ZynqBerryZero_153_201213.png

現在の制約ファイルを示す。
ZynqBerryZero_154_201213.png

これでやってみたのだが、同様に画像が真っ赤だった。ビットストリームが更新されていないのだろうか?
今の所、原因は分かっていない?
  1. 2020年12月15日 04:32 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する3(Vivado 2020.2編1)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する2(Vitis HLS 2020.2編2)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、Vitis HLS 2020.2 の lap_filter_axis プロジェクトを作成し、 C コードの合成、 C/RTL 協調シミュレーション、 Export RTL を行って、 IP を作成した。今回は、 IP を 800 ピクセル X 600 行に変更し(現在は、 64 ピクセル X 48 行)、 C コードの合成、 Export RTL を行って IP を修正してから、その IP を Vivado 2020.2 で使用して、回路を作成する。

まずは、 lap_filter_axis.h を 800 ピクセル X 600 行ラプラシアン・フィルタ処理するように修正する。
ZynqBerryZero_149_201213.png

再度、 C コードの合成を行った。
ZynqBerryZero_150_201213.png

Export RTL を行った。
ZynqBerryZero_151_201213.png

Vivado で回路を作成する前に、ブロック図を貼っておく。
ZynqBerryZero_155_201214.png

この回路では、 lap_filter_axis IP 以外に 2 つの Vivado HLS で作成された IP を使用している。
1 つは、メモリから DMA したデータを AXI4 Stream に変換する IP だ。この IP (DMA2axis)は 2 つの AXI4 Stream 出力を持っていて、引数によりどちらに出力するかを決定することができる。一方の出力は、 AXI4 Stream から DMA に出力する IP (Axis2dma2st)に出力する。つまり元画像を表示するモードだ。もう一方の出力は、ラプラシアン・フィルタ IP (lap_filter_axis)を通って、エッジ画像を表示する。
DMA 出力をディスプレイ・コントローラー IP (bitmap_disp_cntrler_axi_master_v1_0)が DMA して HDMI 信号に変換する。

Vivado 2020.2 を起動して、 Create Project をクリックする。

New Project ダイアログが表示された。設定をしたダイアログを示す。

Project Name ウインドウでは、 Project location を設定し、 Project name を lap_filter_axis_test にした。
ZynqBerryZero_115_201213.png

Default Part では、 ZYNQ07 TE0727 を選択した。
ZynqBerryZero_116_201213.png

最後の New Project Summary を示す。
ZynqBerryZero_117_201213.png

lap_filter_axis_test プロジェクトが作成された。
ZynqBerryZero_118_201213.png

まずは、IP を IP Catalog に追加する。

lap_fiter_axis_test ディレクトリに lap_filter_axis ディレクトリを新規作成して、Vitis HLS 2020.2 で作成した lap_filter_axis_202 ディレクトリの下の solution1/impl ディレクトリ下の export.zip を展開する。
ZynqBerryZero_119_201213.png

DMA2axis IP 、 Axis2dma2st IP 、 bitmap_disp_cntrler_axi_master_v1_0 IP を lap_fiter_axis_test ディレクトリにコピーした。

Vivado 2020.2 の PROJECT MANAGER で IP Catalog をクリックした。
IP Catalog ウインドウが開いた。
IP Catalog ウインドウ内で、右クリックし、右クリックメニューから Add Repository... を選択する。
ZynqBerryZero_120_201213.png

Repositories ウインドウが開く。
lap_filter_axis を選択した。(最初はその他の IP をコピーするのを忘れてしまっていた)
Select ボタンをクリックする。
ZynqBerryZero_121_201213.png

Add Repository ダイアログが開く。
Lap_filter_axis IP が追加されたのが分かる。
ZynqBerryZero_122_201213.png

Lap_filter_axis IP が追加されている。
ZynqBerryZero_123_201213.png

DMA2axis IP 、 Axis2dma2st IP 、 bitmap_disp_cntrler_axi_master_v1_0 IP を同様に追加した。
ZynqBerryZero_124_201213.png

ブロックデザインを作成する。

Vivado 2020.2 の Flow Navigator -> IP INTEGRATOR -> Create Block Design をクリックする。
Create Block Design が表示される。
Design name に lap_filter_axis_bd と入力した。
ZynqBerryZero_125_201213.png

IP を Add IP し、ブロックデザインを完成させた。
ZynqBerryZero_126_201213.png

processing_systemp_7_0 の使用する AXI ポートは、 M_AXI_GP0 の他に S_AXI_HP0 を生かした。

processing_systemp_7_0 の PL へのクロックの設定を示す。
FCLK_CLK0 は 100 MHz に設定した。
FCLK_CLK1 も生かして、 25 MHz に設定した。
ZynqBerryZero_127_201213.png

Vivado 2020.2 の Flow Navigator -> SYNTHESIS -> Run Synthesis をクリックして、論理合成を行った。
ZynqBerryZero_128_201213.png

論理合成が終了し、Synthesis Completed が表示された。
Open Synthesized Design のラジオボタンをクリックしてから、OK ボタンをクリックする。
ZynqBerryZero_129_201213.png

Synthesized Design が開いた。
ZynqBerryZero_130_201213.png

ここでは、 HDMI の信号をピン配置して、IO STANDARD を指定する。
まずは、ZynqBerryZero の回路図から HDMI に出力している Zynq のピン番号を取得する。

ZynqBerryZero の回路図から関連する部分を引用する。
ZynqBerryZero_131_201213.png

ここでは、 HDMI_TX2_P, HDMI_TX2_N 以外の信号の _P と _N が入れ替わっているようなのだ。

通常は、制約ファイルに _P の信号のピン配置を記述するのだが、 _N の番号を記述した。
IO STANDARD には、 TMDS_33 を指定した。
ZynqBerryZero_132_201213.png

制約ファイルの lap_filter_azis_test.xdc に制約が入力された。( lap_filter_azis_test.xdc のファイル名は指定した)
ZynqBerryZero_133_201213.png

論理合成、インプリメンテーション、ビットストリームの生成を行ったところ、タイミング・エラーが発生した。
ZynqBerryZero_134_201213.png

場所は、 vsyncx をディスプレイのクロック(pclk_buf)から AXI4 インターフェースのクロック(clk_fpga0)に載せ替えている部分のようだ。
その部分を見てみよう。
ZynqBerryZero_135_201213.png

vsync_node を 3段の FF で受けているので、タイミングの心配はしなくて良い。
よって、False Path を制約sファイルに設定した。

set_false_path -from [get_clocks -of_objects [get_pins lap_filter_axis_bd_i/bitmap_disp_cntrler_0/inst/dvi_disp_i/BUFR_pixel_clk_io/O]] -to [get_clocks clk_fpga_0]


これで、再度、論理合成、インプリメンテーション、ビットストリームの生成を行うと、成功した。
ZynqBerryZero_136_201213.png
  1. 2020年12月14日 05:10 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する2(Vitis HLS 2020.2編2)

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する1(Vitis HLS 2020.2編1)”の続き。

ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみようということで、前回は、Vitis HLS 2020.2 の lap_filter_axis プロジェクトを作成し、ソースコードやテストベンチを作成して、 C シミュレーションを行った。今回は、C コードの合成、 C/RTL 協調シミュレーション、 Export RTL を行って、 IP を作成する。

Vitis HLS 2020.2 の Project メニューから Project Settings... を選択する。
Project Settings ダイアログが開く。
Synthesis をクリックして、Top Function に lap_filter_axis 関数を設定する。
ZynqBerryZero_103_201212.png

Solution メニューから Run C Synthesis -> C Synthesis を選択して、 C コードの合成を行う。
C コードの合成が成功した。結果を示す。
ZynqBerryZero_104_201212.png

ZynqBerryZero_105_201212.png

タイミング・バイオレーションと言われているが、大丈夫そうだ。
Analysis を見てみよう。
ZynqBerryZero_107_201212.png

タイミング・バイオレーション部分を見ると、最初に読み込み済みのストリート・データを読み飛ばすところだった。

次に、 Solution メニューから Run C/RTL Cosimulation を選択して、 C/RTL 協調シミュレーションを行う。
Co-simulation Dialog が表示された。
Dump Trace を all に設定した。
ZynqBerryZero_108_201212.png

C/RTL 協調シミュレーションが成功した。
Latency は 3089 クリックだった。 64 ピクセル X 48 行のフォントなので、総計 3072 ピクセルだ。よって、 3089 / 3072 ≒ 1.01 クロック/ピクセルとなった。性能が良いと思う。
ZynqBerryZero_109_201212.png

C/RTL 協調シミュレーションの波形を見ても、 ins_TREDY, outs_TVALID 共にほとんど 1 なので、スループットが良いということが言える。
ZynqBerryZero_110_201212.png

Solution メニューから Export RTL を選択した。
Export RTL ダイアログが開く。
Vivado synthesis, place and route にチェックを入れた。
ZynqBerryZero_111_201212.png

Export RTL の結果を示す。
CP achieved post-implementation が 7.824 ns なので、大丈夫そうだ。
ZynqBerryZero_112_201212.png

lap_filter_axis_202 ディレクトリの下の solution1/impl ディレクトリに export.zip と solution1/impl/ip ディレクトリに xilinx_com_hls_lap_filter_axis_1_0.zip ができているが、これらのファイルの違いは何だろう?
ZynqBerryZero_114_201213.png

双方ともにディレクトリを作成して解凍し、比較したのだが、同じものだった。
ZynqBerryZero_113_201213.png
  1. 2020年12月13日 09:32 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0

ZynqBerryZero で HDMI にラプラシアン・フィルタ画像を出力する1(Vitis HLS 2020.2編1)

ZynqBerryZero で L チカができたので、次は HDMI に画像を出力したい。ついでにラプラシアン・フィルタを実装してみよう。
まずは、ZynqBerryZero 用のラプラシアン・フィルタを Vitis HLS 2020.2 で実装してみよう。

Ubuntu 18.04 LTS で Vitis HLS 2020.2 を起動した。
ZynqBerryZero_81_201212.png

Create Project をクリックして、プロジェクトを作成する。

New Vitis HLS Project ダイアログが開く。
Project Configuration では、プロジェクトのロケーションとプロジェクト名を設定する。
Project name に lap_filtger_axis_202 を指定した。
ZynqBerryZero_82_201212.png

Add/Remove Design Files はブランクのままにした。
ZynqBerryZero_83_201212.png

Add/Remove Testbench Files もブランクのままにした。
ZynqBerryZero_84_201212.png

Solution Configuration では、Part Selection を指定する。
Part Selection の ... ボタンをクリックする。
ZynqBerryZero_85_201212.png

ZynqBerryZero のボード・ファイルを入れてあるので、Board をクリックして、Vender に trenz.biz を指定する。
Display Name に ZYNQ-7 TE0727-*-41C34/-010-1C を指定する。
ZynqBerryZero_86_201212.png

OK ボタンをクリックすると、Solution Configuration の Part Selection に ZYNQ-7 TE0727-*-41C34/-010-1C が指定された。
ZynqBerryZero_87_201212.png

Finish ボタンをクリックすると、Vitis HLS 2020.2 のプロジェクトが起動した。
ZynqBerryZero_88_201212.png

ソースコードとテストベンチを生成しよう。

Project メニューから New Source... を選択する。
ZynqBerryZero_89_201212.png

lap_filter_axis.cpp を新規作成する。
ZynqBerryZero_90_201212.png

lap_filter_axis.cpp を入力した。
ZynqBerryZero_91_201212.png

同様に lap_filter_axis.h を入力した。
ZynqBerryZero_92_201212.png

Project メニューから New Test Bench... を選択して、テストベンチ・ファイルを追加する。
ZynqBerryZero_93_201212.png

lap_filter_axis_tb.cpp を新規作成する。
ZynqBerryZero_94_201212.png

lap_filter_axis_tb.cpp を入力した。
ZynqBerryZero_95_201212.png

同様に bmp_header.h を入力した。
ZynqBerryZero_96_201212.png

フォントは自分で作成した。
64 ピクセル x 48 行の C のフォントを自分で書いて作成した。
ZynqBerryZero_97_201212.png

Test Bench に test.bmp として登録した。
ZynqBerryZero_98_201212.png

C シミュレーションを行う。

C Simulation を選択して、 C シミュレーションを行う。
ZynqBerryZero_99_201212.png

C Simulation Dialog が表示された。
OK ボタンをクリックする。
ZynqBerryZero_100_201212.png

C シミュレーション結果を示す。
ZynqBerryZero_101_201212.png

temp_lap.bmp を見るとエッジが表示された。成功だ。
ZynqBerryZero_102_201212.png

lap_filter_axis.cpp を示す。

//
// lap_filter_axis.cpp
// 2015/05/01
// 2015/06/25 : 修正、ラプラシアンフィルタの値が青だけになっていたので、RGBに拡張した
// 2020/12/12 : 修正、ラプラシアンフィルタをマイナスのエッジを表示するように変更した
//

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

#include "lap_filter_axis.h"

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

int lap_filter_axis(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> lap;

    unsigned int line_buf[2][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    int pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete

    int lap_fil_val;

    Loop1 : do {    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    Loop2 : for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        Loop3 : for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            Loop4 : for (int k=0; k<3; k++){
                Loop5 : for (int m=0; m<2; m++){
#pragma HLS UNROLL
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            int y_val = conv_rgb2y(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            lap_fil_val = laplacian_fil(    pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                        pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                        pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる

            if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
                lap.data = 0;

            if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                lap.user = 1;
            else
                lap.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            outs << lap;    // AXI4-Stream へ出力
        }
    }

    return 0;
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = -y;
    else if (y>255)
        y = 255;
    return(y);
}


lap_filter_axis.h を示す。

// lap_filter_axis.h
// 2015/05/01

//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


bmp_header.h を示す。

// bmp_header.h
// BMP ファイルフォーマットから引用させて頂きました
// http://www.kk.iij4u.or.jp/~kondo/bmp/
//
// 2017/05/04 : takseiさんのご指摘によりintX_tを使った宣言に変更。takseiさんありがとうございました
//              変数の型のサイズの違いによってLinuxの64ビット版では動作しなかったためです
//              http://marsee101.blog19.fc2.com/blog-entry-3354.html#comment2808
//

#include <stdio.h>
#include <stdint.h>

// BITMAPFILEHEADER 14bytes
typedef struct tagBITMAPFILEHEADER {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
} BITMAPFILEHEADER;

// BITMAPINFOHEADER 40bytes
typedef struct tagBITMAPINFOHEADER{
    uint32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t biXPixPerMeter;
    int32_t biYPixPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImporant;
} BITMAPINFOHEADER;

typedef struct BMP24bitsFORMAT {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} BMP24FORMAT;


lap_filter_axis_tb.cpp を示す。

// lap_filter_axis_tb.cpp
// 2015/05/01
// 2015/08/17 : BMPファイルを読み書きするように変更した
// 2020/12/12 : 修正、ラプラシアンフィルタをマイナスのエッジを表示するように変更した
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>

#include "lap_filter_axis.h"
#include "bmp_header.h"

int lap_filter_axis(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);

int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y_soft(int rgb);
int lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height);

#define CLOCK_PERIOD 10

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs;
    hls::stream<ap_axis<32,1,1,1> > outs_soft;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;
    ap_axis<32,1,1,1> vals_soft;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;

    if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_lapd =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_lapd memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // ins に入力データを用意する
    for(int i=0; i<5; i++){ // dummy data
        pix.user = 0;
        pix.data = i;
        ins << pix;
    }

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            pix.data = (ap_int<32>)rd_bmp[(j*bmpihr.biWidth)+i];

            if (j==0 && i==0)   // 最初のデータの時に TUSER を 1 にする
                pix.user = 1;
            else
                pix.user = 0;

            if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
                pix.last = 1;
            else
                pix.last = 0;

            ins << pix;
            ins_soft << pix;
        }
    }

    lap_filter_axis(ins, outs);
    lap_filter_axis_soft(ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    cout << endl;
    cout << "outs" << endl;
    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_int<32> val = vals.data;
            ap_int<32> val_soft = vals_soft.data;

            hw_lapd[(j*bmpihr.biWidth)+i] = (int)val;

            if (val != val_soft){
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %d, SW = %d\n", i, j, (int)val, (int)val_soft);
                return(1);
            }
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    // ハードウェアのラプラシアンフィルタの結果を temp_lap.bmp へ出力する
    if ((fbmpw=fopen("temp_lap.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

    // RGB データの書き込み、逆順にする
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);
    free(rd_bmp);
    free(hw_lapd);

    return 0;
}

int lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> lap;
    unsigned int **line_buf;
    int pix_mat[3][3];
    int lap_fil_val;
    int i;

    // line_buf の1次元目の配列をアロケートする
    if ((line_buf =(unsigned int **)malloc(sizeof(unsigned int *) * 2)) == NULL){
        fprintf(stderr, "Can't allocate line_buf[3][]\n");
        exit(1);
    }

    // メモリをアロケートする
    for (i=0; i<2; i++){
        if ((line_buf[i]=(unsigned int *)malloc(sizeof(unsigned int) * width)) == NULL){
            fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
            exit(1);
        }
    }

    do {    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (int y=0; y<height; y++){
        for (int x=0; x<width; x++){
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            for (int k=0; k<3; k++){
                for (int m=0; m<2; m++){
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            int y_val = conv_rgb2y_soft(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            lap_fil_val = laplacian_fil_soft(    pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                        pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                        pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる

            if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
                lap.data = 0;

            if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                lap.user = 1;
            else
                lap.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            outs << lap;    // AXI4-Stream へ出力
        }
    }

    for (i=0; i<2; i++)
        free(line_buf[i]);
    free(line_buf);

    return 0;
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = -y;
    else if (y>255)
        y = 255;
    return(y);
}

  1. 2020年12月12日 21:40 |
  2. ZynqBerryZero
  3. | トラックバック:0
  4. | コメント:0
»