FC2カウンター FPGAの部屋 2023年10月
fc2ブログ

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

FPGAの部屋

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

Vitis Unified IDE 2023.2 の HLS Development を試す4

Vitis Unified IDE 2023.2 の HLS Development を試す3”の続き。

Vitis Unified IDE 2023.2 の HLS Development を試してみようということで、前回は、hls_config.cfg に OpenCV のインクルードパスとリンカー・オプションを設定したところ C シミュレーションが通った。今回は、C コードの合成を行った。Function Call Graph を表示しようとしたらフリー・ライセンスが必要ということなので、ライセンスを取得したら、Function Call Graph、Schedule Viewer を表示することができた。

FLOW の C SYNTHESIS -> Run をクリックして、C コードの合成を行って成功した。
Vivado_2023_2_24_231029.png

FLOW の C SYNTHESIS -> REPORTS -> Summary を表示した。
Vivado_2023_2_26_231030.png

FLOW の C SYNTHESIS -> REPORTS -> Synthesis を表示した。
Vivado_2023_2_27_231030.png

C コードの合成の結果を示す。
Vivado_2023_2_28_231030.png
Vivado_2023_2_29_231030.png

Vitis HLS 2023.1 の結果を示す。
zub1cg_i5filters_60_231025.png
zub1cg_i5filters_61_231025.png

リソース使用量はそう変化が無いが、Estimated の値は、Vitis Unified IDE 2023.2 が 4.833 ns に対して、Vitis HLS 2023.1 は 7.146 ns と違いがある。

FLOW の C SYNTHESIS -> REPORTS -> Function Call Graph を表示した。しかし、表示されない。Function Call Graph の表示には、free Vitis HLS license が必要だそうだ。
Vivado_2023_2_30_231030.png

素直に、http://www.xilinx.com/getlicense をクリックしたところ、ブラウザが立ち上がったが、サインインしてもライセンス取得に行けなかった。
Vivado_2023_2_31_231030.png

Chrome で http://www.xilinx.com/getlicense を表示して、サインインしたところ、ライセンス取得画面に行けたので、2023 AI Engine License と Vitis HLS License を取得した。
Vivado_2023_2_32_231030.png

HostName と Ethernet MAC を入力して、メールでライセンスを取得した。
Vivado_2023_2_33_231030.png

Vitis 2023.2 の環境設定を行ったターミナルで vlm を起動する。
Vivado_2023_2_34_231030.png

Vivado License Manager 2023.2 が起動する。
Get License -> Load License をクリックし、Copy License... ボタンをクリックする。
Vivado_2023_2_35_231030.png

ダウンロードしたライセンス・ファイルを指定する。

Manage License Setup をクリックして、Certificate Base License を見ると AI Engine のライセンスが見えた。
Vivado_2023_2_36_231030.png

Vitis HLS のライセンスも見える。
Vivado_2023_2_37_231030.png

もう一度、FLOW の C SYNTHESIS -> REPORTS -> Function Call Graph をクリックすると、表示された。
Vivado_2023_2_38_231030.png

FLOW の C SYNTHESIS -> REPORTS -> Schedule Viewer を表示した。
Vivado_2023_2_39_231030.png

FLOW の C SYNTHESIS -> REPORTS -> Kernel Guidance を表示した。チェックボックスにチェックを入れてある。
Vivado_2023_2_40_231030.png
  1. 2023年10月31日 04:40 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis Unified IDE 2023.2 の HLS Development を試す3

Vitis Unified IDE 2023.2 の HLS Development を試す2”の続き。

Vitis Unified IDE 2023.2 の HLS Development を試してみようということで、前回は、hls_config.cfg と vitis-comp.json を見て、ソースファイルとテストベンチファイル、画像ファイルをコピーした。コピーしたファイルを Vitis Unified IDE に登録して、C シミュレーションを行ったが、エラーになった。今回は、hls_config.cfg に OpenCV のインクルードパスとリンカー・オプションを設定したところ C シミュレーションが通った。

C シミュレーション時に OpenCV を使用するための設定を行う。

Vitis Unified IDE 2023.2 で、左の VITIS COMPONENTS の HSVConverter -> Settings -> hls_config.cfg をクリックすると設定が行えるようだ。
C Sythesis sources の top に HSVConverter を設定した。これは、C コードの合成時にハードウェアとする関数を指定する。
Testbench sources の CFLAGS に -I/usr/local/include を指定した。
Vivado_2023_2_20_231029.png

C Simulation の ldflags に -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc を指定した。
Vivado_2023_2_21_231029.png

FLOW の C SIMULATION -> Run をクリックすると C Simulation が実行され、成功した。
Vivado_2023_2_22_231029.png

Vitis Unified IDE 2023.2 では、printf() で出力した文字列は OUTPUT のみに出力されている。
Summary を表示した。
Vivado_2023_2_23_231029.png

HSVConverter プロジェクトのディレクトリの下に HSVConverter ディレクトリが生成され、その下の hls ディレクトリの下に csim ディレクトリが生成されている。
csim/build ディレクトリを見ると、hsv_converter.jpg と hls_converter2.jpg が生成されていた。
Vivado_2023_2_25_231029.png
  1. 2023年10月30日 05:37 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis Unified IDE 2023.2 の HLS Development を試す2

Vitis Unified IDE 2023.2 の HLS Development を試す1”の続き。

Vitis Unified IDE 2023.2 の HLS Development を試してみようということで、前回は、HLS コンポーネントの HSVConverter プロジェクトを作成することにした。やってみたところ、プロジェクトが作れなかったが、Settings 画面の項目を全て記入したところ HSVConverter プロジェクトを作成することができた。今回は、hls_config.cfg と vitis-comp.json を見て、ソースファイルとテストベンチファイル、画像ファイルをコピーした。コピーしたファイルを Vitis Unified IDE に登録して、C シミュレーションを行ったが、エラーになった。

まずは、HSVConverter プロジェクトの HSVConverter ディレクトリに生成された 2 つのファイル、hls_config.cfg と vitis-comp.json を見てみよう。
hls_config.cfg を示す。

part=xczu1cg-sbva484-1-e

[hls]
flow_target=vivado
package.output.format=ip_catalog
package.output.syn=false
clock=10ns
clock_uncertainty=27%


vitis-comp.json を示す。

{
  "name": "HSVConverter",
  "type": "HLS",
  "configuration": {
    "componentType": "HLS",
    "configFiles": [
      "hls_config.cfg"
    ],
    "work_dir": "HSVConverter"
  },
  "previousBuildStatusMap": {}
}


HSVConverter ディレクトリにソースファイルやテストベンチファイル、画像ファイルをコピーした。
Vivado_2023_2_13_231029.png

Vitis Unified IDE でも、左側の VITIS COMPONENTS の Source や Test Bench から右クリックで Add Source File や Add Test Bench File を選んでファイルを追加できる。
Vivado_2023_2_14_231029.png

ソースファイルとテストベンチファイルを追加した。
なお、ディレクティブ・エディタは View メニューの HLS Directive にあった。ディレクティブ・エディタを一度表示すると、右のドックに入って、次からそのアイコンをクリックすると使えるようになるようだ。
Vivado_2023_2_15_231029.png

ディレクティブ・エディタも以前同様にディレクティブを設定できるようだ。
Vivado_2023_2_16_231029.png

View メニューから Vitis log を表示した。
Vivado_2023_2_17_231029.png

Flow の下が表示できなかったが、全画面表示にしたら、C SIMULATION などが出てきた。上に持ってくれば良いようだ。
Vivado_2023_2_18_231029.png

C SIMULATION の Run をクリックして C シミュレーションしてみたところエラーだった。
そうだ。このプロジェクトでは OpenCV の設定をする必要があった。設定をするのはどこだろう?
Vivado_2023_2_19_231029.png
  1. 2023年10月29日 05:06 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis Unified IDE 2023.2 の HLS Development を試す1

Vitis Unified IDE 2023.2 の HLS Development を試してみようということで、HLS コンポーネントの HSVConverter プロジェクトを作成することにした。しかし、プロジェクトが作れなかったが、Settings 画面の項目を全て記入したところ HSVConverter プロジェクトを作成することができた。

Vitis 2023.2 をインストールした

viits & で Vitis 2023.2 を起動した。

HLS Development -> Create Component をクリックして、HLS Component を作成する。
Vivado_2023_2_2_231028.png

Create HLS Component - Empty HLS Component ダイアログが表示された。
Name and Location 画面で、Component name に HSVConverter を入力し、Component location を入力した。
Vivado_2023_2_3_231028.png

Configuration File 画面
デフォルトのままとした。
Vivado_2023_2_4_231028.png

Source Files 画面
ここもデフォルトのままとした。
Vivado_2023_2_5_231028.png

Hardware 画面
Part で xzu1cg-sva484-1-e を選択した。
Vivado_2023_2_6_231028.png

Settings 画面
clock に 10ns を入力した。
Vivado_2023_2_7_231028.png

Summary 画面
Finish ボタンをクリックした。
Vivado_2023_2_8_231028.png

Welcome to the Vitis Unified IDE 画面に戻ってしまった。
Vivado_2023_2_9_231028.png

ワークスペースのディレクトリにも logs ディレクトリしか無いのはなぜだ?
Vivado_2023_2_10_231028.png

”Vitis High-Level Synthesis User Guide (UG1399)”の 2023.2 版の”Building and Running an HLS Component”にもプロジェクトができそうなことが書いてあるけど?

(追記)
Settings 画面で clock_uncertainty27% を入力したら、HSVConverter プロジェクトが生成された。
Settings 画面はすべて入力する必要があるようだ。
Vivado_2023_2_11_231028.png

ワークスペースに HSVConverter ディレクトリが生成された。
Vivado_2023_2_12_231028.png
  1. 2023年10月28日 06:01 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

HSV の値を変更する HSVConverter を作る4

HSV の値を変更する HSVConverter を作る3”の続き。

前回は、HSVConverter の C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

C コードの合成を行った。結果を示す。
zub1cg_i5filters_60_231025.png
zub1cg_i5filters_61_231025.png

レイテンシもリソース消費量も問題無さそうだ。

C/RTL 協調シミュレーションを行った。結果を示す。
zub1cg_i5filters_62_231027.png

C/RTL 協調シミュレーションの全体波形を示す。
こちらも、TVALID 、TREADY がほとんど 1 で問題無さそうだ。
zub1cg_i5filters_63_231027.png

Export RTL を行った。

Implementation を行った。結果を示す。
zub1cg_i5filters_64_231027.png

zub1cg_i5filters_65_231027.png
  1. 2023年10月27日 05:07 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado 2023.2 が出ています

X の K林さんのツィートで知ったのですが、Vivado 2023.2 が出ています

Vivado も Vitis HLS も見かけは 2023.1 と変わりませんが、Vitis を起動したところ変更が大きいようです。
Vivado_2023_2_1_231026.png

マニュアル読んで、使い方を学習しようと思います。
  1. 2023年10月26日 05:07 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

”実践的!FPGA開発セミナー vol.27”で発表します

今日、”実践的!FPGA開発セミナー vol.27”が開催されて、そこで、お話させていただきます。

事前に発表用スライドの”PYNQ上で画像用自作ハードウェアを動作させる”を公開します。

スライドには該当するFPGAの部屋のブログ記事へのリンクが張ってあるので、リンクをクリックしてより詳しい説明をご覧ください。

本日はよろしくお願いいたします。

(追記) ishiwatari さんの質問のお返事
共有物理メモリは、CMA 領域に取られるメモリ領域のようです。(”Pynq & maximum allocatable memory (CMA)”参照)

なお、allocate() の説明は、”Allocate”や”pynq.buffer Module”にあります。
  1. 2023年10月25日 04:09 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

HSV の値を変更する HSVConverter を作る3

HSV の値を変更する HSVConverter を作る2”の続き。

前回は、ソースコードの HSVConverter.cpp とテストベンチの HSVConverter_tb.cpp を示して、Vitis HLS 2023.1 の HSVConverter プロジェクトを作成した。今回は、C シミュレーションを行った。

C シミュレーションを行った。結果を示す。
zub1cg_i5filters_53_231023.png

solution1/csim/build ディレクトリを示す。
zub1cg_i5filters_54_231023.png

元画像の test2.jpg を示す。
zub1cg_i5filters_59_231024.jpg

H に180 を加えて、S と V を 2 倍した hsv_converter.jpg を示す。
zub1cg_i5filters_55_231023.jpg

S と V に 32 を足した hsv_converter2.jpg を示す。
zub1cg_i5filters_56_231023.jpg

今日は寝坊してしまったので、ここまでとする。
  1. 2023年10月24日 05:23 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

”実践的!FPGA開発セミナー vol.27”で発表します

今週の水曜日(2023/10/25)に”実践的!FPGA開発セミナー vol.27”で発表します。
タイトルは”PYNQ上で画像用自作ハードウェアを動作させる”です。
ZYNQ ボードで動作する PYNQ で画像用の自作ハードウェアを動作させる方法です。私のハマった 2 つのハマるポイントについて説明し、解決方法を解説します。

水曜日(2023/10/25)の朝に発表用 PPT の PDF 版を公開する予定です。
  1. 2023年10月23日 03:55 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

HSV の値を変更する HSVConverter を作る2

HSV の値を変更する HSVConverter を作る1”の続き。

前回は、これまで、RGB-HSV 変換 IP と HSV-RGB 変換 IP を作ってきたが、HSV の値を変更する HSVConverter IP を Vitis HLS 2023.1 で作成しようということで、概要を述べた。今回は、ソースコードの HSVConverter.cpp とテストベンチの HSVConverter_tb.cpp を示して、Vitis HLS 2023.1 の HSVConverter プロジェクトを作成する。

最初に HSVConverter.h を示す。

// HSVConverter.h
// 2023/10/21 by marsee
//

#ifndef __HSVCONVERTER_H__
#define __HSVCONVERTER_H__

#define ORG_IMGwAxiVdma 0
#define HSV_CONVwAxiVdma 1
#define ORG_IMGwAxiDma 2
#define HSV_CONVwAxiDma 3

#endif


ソースコードの HSVConverter.cpp を示す。

// HSVConverter.cpp
// 2023/10/21 by marsee
//

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <ap_fixed.h>

#include "HSVConverter.h"

int hsv_conv(ap_uint<32> data, ap_uint<32> &val, int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_ad);
int separate_hsv(ap_uint<25> hsv, ap_uint<9> &h, ap_uint<8> &s, ap_uint<8> &v);

int HSVConverter(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_axiu<32,1,1,1> >& outs, int32_t function,
        int32_t row_size, int32_t col_size,
        int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add){
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=h_add
#pragma HLS INTERFACE mode=s_axilite port=s_mult
#pragma HLS INTERFACE mode=s_axilite port=s_add
#pragma HLS INTERFACE mode=s_axilite port=v_mult
#pragma HLS INTERFACE mode=s_axilite port=v_add

    ap_axiu<32,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvd;
    ap_uint<32> val;

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

    LOOP_Y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        LOOP_X: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            hsv_conv(pix.data, val, h_add, s_mult, s_add, v_mult, v_add);
            hsvd.data = val;

            if(function==ORG_IMGwAxiVdma || function == HSV_CONVwAxiVdma){
                if(x==0 && y==0) // 最初のピクセル
                    hsvd.user = 1;
                else
                    hsvd.user = 0;
                if(x == (col_size-1)) // 行の最後
                    hsvd.last = 1;
                else
                    hsvd.last = 0;
            }else{
                hsvd.user = 0;
                hsvd.last = pix.last;
            }
            hsvd.keep = 0x7;
            hsvd.strb = 0x7;
            if(function==HSV_CONVwAxiVdma || function==HSV_CONVwAxiDma)
                outs << hsvd;
            else
                outs << pix;
        }
    }
    return(0);
}

// hsv_converter
// h に h_add を加算する
// s に
int hsv_conv(ap_uint<32> data, ap_uint<32> &val, int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add){
    ap_uint<9> h;
    ap_uint<8> s , v;
    int32_t calc_h;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> calc_st, calc_vt, ap_05;
    int32_t calc_s, calc_v;
    ap_uint<9> ht;
    ap_uint<8> st, vt;

    ap_05 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)0.5;
    separate_hsv(data, h, s, v);
    calc_h = (h + h_add)%360;
    if(calc_h < 0)
        calc_h = 360 + calc_h;
    calc_st = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)s * s_mult;
    calc_s = (uint32_t)(calc_st + ap_05) + s_add;
    calc_vt = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)v * v_mult;
    calc_v = (uint32_t)(calc_vt + ap_05) + v_add;
    if(calc_s < 0)
        calc_s = 0;
    else if(calc_s > 255)
        calc_s = 255;
    if(calc_v < 0)
        calc_v = 0;
    else if(calc_v > 255)
        calc_v = 255;
    ht = (ap_uint<9>)calc_h;
    st = (ap_uint<8>)calc_s;
    vt = (ap_uint<8>)calc_v;

    val = ((ap_uint<32>)ht << 16)+((ap_uint<32>)st << 8)+(ap_uint<32>)vt;
    return(0);
}

// separate_hsv
// HSVを分離する
// HSVのフォーマットは、{H(9bits), S(8bits), V(8bits)}, 1pixel = 32bits
//
int separate_hsv(ap_uint<25> hsv, ap_uint<9> &h, ap_uint<8> &s, ap_uint<8> &v){
    v = (ap_uint<8>)(hsv & 0x1ff);
    s = (ap_uint<8>)((hsv>>8) & 0xff);
    h = (ap_uint<9>)((hsv>>16) & 0x1ff);
    return(0);
}


テストベンチの HSVConverter_tb.cpp を示す。

// HSVConverter_tb.cpp
// 2023/10/21 by marsee
//

// HSVCwXilinxVideoStandard を define すると axi_vdma 用となり、コメントアウトすると axi_dma 用になる
//

#include <stdio.h>
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"

#include "HSVConverter.h"

#define HSVCwXilinxVideoStandard

constexpr int size = 3;

int HSVConverter(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_axiu<32,1,1,1> >& outs, int32_t function,
        int32_t row_size, int32_t col_size,
        int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add);
int HSVConverter_soft(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_axiu<32,1,1,1> >& outs, int32_t function,
        int32_t row_size, int32_t col_size,
        int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add);
int hsv_conv_soft(ap_uint<32> data, ap_uint<32> &val, int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_ad);
int separate_hsv_soft(ap_uint<25> hsv, ap_uint<9> &h, ap_uint<8> &s, ap_uint<8> &v);
int32_t RGB2HSV(int32_t rgb);
int32_t HSV2RGB(int32_t hsv);

const char INPUT_JPG_FILE[] = "test2.jpg";
const char OUTPUT_JPG_FILE[] = "hsv_converter.jpg";
const char ORG_OUT_JPG_FILE[] = "hsv_converter2.jpg";

int main(){
    hls::stream<ap_axiu<32,1,1,1> > ins, ins2;
    hls::stream<ap_axiu<32,1,1,1> > ins_soft;
    hls::stream<ap_axiu<32,1,1,1> > outs, outs2;
    hls::stream<ap_axiu<32,1,1,1> > outs_soft;
    ap_axiu<32,1,1,1> pix;
    ap_axiu<32,1,1,1> vals, vals_soft;

    // JPG ファイルをMat に読み込む
    cv::Mat img = cv::imread(INPUT_JPG_FILE);

    // ピクセルを入れる領域の確保
    std::vector<int32_t> rd_bmp(sizeof(int32_t)*img.cols*img.rows);
    std::vector<int32_t> hw_hsv_conv(sizeof(int32_t)*(img.cols)*(img.rows));
    std::vector<int32_t> sw_hsv_conv(sizeof(int32_t)*(img.cols)*(img.rows));

    // rd_bmp にJPGのピクセルを代入
    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            cv::Vec3b pixel;
            pixel = dst_vec3b(y,x);
            rd_bmp[y*img.cols+x] = (pixel[0] & 0xff) | ((pixel[1] & 0xff)<<8) | ((pixel[2] & 0xff)<<16); // RGB 8 bits
            // blue - pixel[0]; green - pixel[1]; red - pixel[2];
        }
    }

#ifdef HSVCwXilinxVideoStandard
    // ins に入力データを用意する
    for(int i=0; i<5; i++){ // dummy data
        pix.user = 0;
        pix.data = i;
        pix.last = 0;
        pix.user = 0;
        pix.keep = 0x7;
        pix.strb = 0x7;
        ins << pix;
    }
#endif

    for(int j=0; j < img.rows; j++){
        for(int i=0; i < img.cols; i++){
            pix.data = RGB2HSV((int32_t)rd_bmp[(j*img.cols)+i]);
#ifdef HSVCwXilinxVideoStandard
            if (j==0 && i==0)   // 最初のデータの時に TUSER を 1 にする
                pix.user = 1;
            else
                pix.user = 0;

            if (i == img.cols-1) // 行の最後でTLASTをアサートする
                pix.last = 1;
            else
                pix.last = 0;
#else
            if(j==img.rows-1 && i==img.cols-1)
                pix.last = 1;
            else
                pix.last = 0;
            pix.user = 0;
#endif
            pix.keep = 0x7;
            pix.strb = 0x7;

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

    // Hに180を加えて、SとVは2倍する
#ifdef HSVCwXilinxVideoStandard
    HSVConverter(ins, outs, HSV_CONVwAxiVdma, img.rows, img.cols, 180, 2.0, 0, 2.0, 0); // ハードウェア
    HSVConverter_soft(ins_soft, outs_soft, HSV_CONVwAxiVdma, img.rows, img.cols, 180, 2.0, 0, 2.0, 0);  // ソフトウェア
#else
    HSVConverter(ins, outs, HSV_CONVwAxiDma, img.rows, img.cols, 180, 2.0, 0, 2.0, 0); // ハードウェアのメディアンフィルタ
    HSVConverter_soft(ins_soft, outs_soft, HSV_CONVwAxiDma, img.rows, img.cols, 180, 2.0, 0, 2.0, 0);  // ソフトウェアのメディアンフィルタ
#endif

    // ハードウェアとソフトウェアのメディアンフィルタの値のチェック
    for (int y=0; y<img.rows; y++){ // 結果の画像サイズはx-2, y-2
        for (int x=0; x<img.cols; x++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_uint<32> val = vals.data;
            hw_hsv_conv[y*img.cols+x] = (int32_t)val;
            if (val != vals_soft.data){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n",
                        x, y, val, vals_soft.data);
                return(1);
            }
        }
    }
    printf("Success HW and SW results match\n");

    const int hsv_conv_row = img.rows;
    const int hsv_conv_cols = img.cols;
    cv::Mat wbmpf(hsv_conv_row, hsv_conv_cols, CV_8UC3);
    // wbmpf に色変換後の画像を入力
    cv::Mat_<cv::Vec3b> sob_vec3b = cv::Mat_<cv::Vec3b>(wbmpf);
    for (int y=0; y<wbmpf.rows; y++){
        for (int x=0; x<wbmpf.cols; x++){
            cv::Vec3b pixel;
            pixel = sob_vec3b(y,x);
            int32_t rbg = HSV2RGB(hw_hsv_conv[y*wbmpf.cols+x]);
            pixel[0] = (rbg & 0xff); // blue
            pixel[1] = ((rbg >> 8) & 0xff); // green
            pixel[2] = ((rbg >> 16) & 0xff); // red
            sob_vec3b(y,x) = pixel;
        }
    }

    // ハードウェアのメディアンフィルタの結果を jpg ファイルへ出力する
    cv::imwrite(OUTPUT_JPG_FILE, wbmpf);

#ifdef HSVCwXilinxVideoStandard
    HSVConverter(ins2, outs2, HSV_CONVwAxiVdma, img.rows, img.cols, 0, 1.0, 32, 1.0, 32);
#else
    HSVConverter(ins2, outs2, HSV_CONVwAxiDma, img.rows, img.cols, 0, 1.0, 32, 1.0, 32);
#endif

    cv::Mat wbmpf2(hsv_conv_row, hsv_conv_cols, CV_8UC3);
    // wbmpf2 に元画像を入力
    sob_vec3b = cv::Mat_<cv::Vec3b>(wbmpf2);
    for (int y=0; y<wbmpf.rows; y++){
        for (int x=0; x<wbmpf.cols; x++){
            cv::Vec3b pixel;
            pixel = sob_vec3b(y,x);
            outs2 >> vals;
            int32_t val = HSV2RGB(vals.data);
            pixel[0] = (val & 0xff); // blue
            pixel[1] = ((val >> 8) & 0xff); // green
            pixel[2] = ((val >> 16) & 0xff); // red
            sob_vec3b(y,x) = pixel;
        }
    }

    // 元画像を jpg ファイルへ出力する
    cv::imwrite(ORG_OUT_JPG_FILE, wbmpf2);

    return(0);
}

int HSVConverter_soft(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_axiu<32,1,1,1> >& outs, int32_t function,
        int32_t row_size, int32_t col_size,
        int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add){

    ap_axiu<32,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvd;
    ap_uint<32> val;

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

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

            hsv_conv_soft(pix.data, val, h_add, s_mult, s_add, v_mult, v_add);
            hsvd.data = val;

            if(function==ORG_IMGwAxiVdma || function == HSV_CONVwAxiVdma){
                if(x==0 && y==0) // 最初のピクセル
                    hsvd.user = 1;
                else
                    hsvd.user = 0;
                if(x == (col_size-1)) // 行の最後
                    hsvd.last = 1;
                else
                    hsvd.last = 0;
            }else{
                hsvd.user = 0;
                hsvd.last = pix.last;
            }
            hsvd.keep = 0x7;
            hsvd.strb = 0x7;
            if(function==HSV_CONVwAxiVdma || function==HSV_CONVwAxiDma)
                outs << hsvd;
            else
                outs << pix;
        }
    }
    return(0);
}

// hsv_converter
// h に h_add を加算する
// s に
int hsv_conv_soft(ap_uint<32> data, ap_uint<32> &val, int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add){
    ap_uint<9> h;
    ap_uint<8> s , v;
    int32_t calc_h;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> calc_st, calc_vt, ap_05;
    int32_t calc_s, calc_v;
    ap_uint<9> ht;
    ap_uint<8> st, vt;

    ap_05 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)0.5;
    separate_hsv_soft(data, h, s, v);
    calc_h = (h + h_add)%360;
    if(calc_h < 0)
        calc_h = 360 + calc_h;
    calc_st = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)s * s_mult;
    calc_s = (uint32_t)(calc_st + ap_05) + s_add;
    calc_vt = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)v * v_mult;
    calc_v = (uint32_t)(calc_vt + ap_05) + v_add;
    if(calc_s < 0)
        calc_s = 0;
    else if(calc_s > 255)
        calc_s = 255;
    if(calc_v < 0)
        calc_v = 0;
    else if(calc_v > 255)
        calc_v = 255;
    ht = (ap_uint<9>)calc_h;
    st = (ap_uint<8>)calc_s;
    vt = (ap_uint<8>)calc_v;

    val = ((ap_uint<32>)ht << 16)+((ap_uint<32>)st << 8)+(ap_uint<32>)vt;
    return(0);
}

// separate_hsv
// HSVを分離する
// HSVのフォーマットは、{H(9bits), S(8bits), V(8bits)}, 1pixel = 32bits
//
int separate_hsv_soft(ap_uint<25> hsv, ap_uint<9> &h, ap_uint<8> &s, ap_uint<8> &v){
    v = (ap_uint<8>)(hsv & 0x1ff);
    s = (ap_uint<8>)((hsv>>8) & 0xff);
    h = (ap_uint<9>)((hsv>>16) & 0x1ff);
    return(0);
}

// RGBからHSVに変換
int32_t RGB2HSV(int32_t rgb){
    int32_t r, g, b;
    int32_t h, s, v;
    float ht, st;
    float max, min;
    int32_t hsv;

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

    // h と max, min を求める
    if(r==g && g==b && r==b){
        max = (float)r; // 8倍
        min = (float)r;
    }else if(r>=g && r>=b){ // r が最大
        max = (float)r;
        if(g>=b)
            min = (float)b;
        else
            min = (float)g;
    }else if(g>=r && g>=b){ // g が最大
        max = (float)g;
        if(r>=b)
            min = (float)b;
        else
            min = (float)r;
    }else{ // b が最大
        max = (float)b;
        if(r>=g)
            min = (float)g;
        else
            min = (float)r;
    }
    if(max-min == 0.0)
        ht = 0.0;
    else if(max == (float)r)
        ht = 60.0 * (((float)g-(float)b)/(max-min));
    else if(max == (float)g)
        ht = 60.0 * (((float)b-(float)r)/(max-min)) + 120.0; // MAGビットシフトして精度を確保
    else // if(max == b)
        ht = 60.0 * (((float)r-(float)g)/(max-min)) + 240.0; // MAGビットシフトして精度を確保

    if(ht < 0)
        ht += 360.0;
    h = (uint32_t)(ht + 0.5);

    if(max == 0.0)
        st = 0.0;
    else
        st = ((max - min)/max) * 255.0; // MAGビットシフトして精度を確保
    s = (uint32_t)(st + 0.5);

    v = (uint32_t)max;

    hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

    return(hsv);
}

// HSVからRGBに変換
int32_t HSV2RGB(int32_t hsv){
    int32_t h, s, v;
    float max, min;
    uint8_t r, g, b;
    int32_t rgb;

    h = (hsv >> 16) & 0x1ff;
    s = (hsv >> 8) & 0xff;
    v = hsv & 0xff;

    max = (float)v;
    min = max - ((float)s / 255.0) * max;

    if(h>=0 && h<60){
        r = (uint8_t)max;
        g = (uint8_t)(((float)h / 60.0) * (max - min) + min + 0.5);
        b = (uint8_t)(min + 0.5);
    }else if(h>=60 && h<120){
        r = (uint8_t)(((120.0 - (float)h) / 60.0) * (max - min) + min + 0.5);
        g = (uint8_t)max;
        b = (uint8_t)(min + 0.5);
    }else if(h>=120 && h<180){
        r = (uint8_t)(min + 0.5);
        g = (uint8_t)max;
        b = (uint8_t)((((float)h - 120.0) / 60.0) * (max - min) + min + 0.5);
    }else if(h>=180 && h<240){
        r = (uint8_t)(min + 0.5);
        g = (uint8_t)(((240.0 - (float)h) / 60.0) * (max - min) + min + 0.5);
        b = (uint8_t)max;
    }else if(h>=240 && h<300){
        r = (uint8_t)((((float)h - 240.0) / 60.0) * (max - min) + min + 0.5);
        g = (uint8_t)(min + 0.5);
        b = (uint8_t)max;
    }else{ // h>=300 && h<=360
        r = (uint8_t)max;
        g = (uint8_t)(min + 0.5);
        b = (uint8_t)(((360.0 - (float)h) / 60.0) * (max - min) + min + 0.5);
    }
    rgb = (r << 16) + (g << 8) + b;

    return(rgb);
}


Vitis HLS 2023.1 で HSVConverter プロジェクトを作成した。
part は xczu1cg-sbva484-1-e を指定した。
zub1cg_i5filters_50_231022.png

今回のテストベンチ・コードでは OpenCV ライブラリを使用している。
Vitis HLS 2021.1 には内蔵された OpenCV は無いので、別にインストールした OpenCV を指定する。
Vitis HLS の Project メニューから Project Settings... を選択して、Project Settings ダイアログを開いた。
Simulation タブを開いて、HSVConverter_tb.cpp の CFLAGS に

-I/usr/local/include

を設定した。
Linker Flags に

-L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

を設定した。
zub1cg_i5filters_51_231022.png

更に、 Synthesis をクリックして、 Top Function に HSVConverter を指定した。
zub1cg_i5filters_52_231022.png

OK ボタンをクリックした。
  1. 2023年10月23日 03:46 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

HSV の値を変更する HSVConverter を作る1

これまで、RGB-HSV 変換 IP と HSV-RGB 変換 IP を作ってきたが、HSV の値を変更する HSVConverter IP を Vitis HLS 2023.1 で作成しよう。

H、色相は乗算する必要はないと思うので、加算だけにする。S、色彩は乗算と加算を行うことにする。V、明度も S と同様に乗算と加算を行うことにする。

HSVConverter() 関数宣言はこうなった。

int HSVConverter(hls::stream<ap_axiu<32,1,1,1> >& ins,
        hls::stream<ap_axiu<32,1,1,1> >& outs, int32_t function,
        int32_t row_size, int32_t col_size,
        int32_t h_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> s_mult, int32_t s_add,
        ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> v_mult, int32_t v_add);


これで、ソースコードとテストベンチを作成しよう。
  1. 2023年10月21日 04:09 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する7

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する6”の続き。

前回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行って、完成とするはずだったが、”Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する3”で、MAG = 16、つまり、小数点以下の桁が 16 ビットのほうが精度が良いとわかったので、今回は、それを修正し、更にテストベンチも浮動小数点数で演算した結果と比較することにした。結果として、任意精度固定小数点数で演算した HSV 値と浮動小数点数で演算した HSV 値の差分が 1 より大きくはないことがわかった。

ソースコードの RGB24toHSV.cpp を示す。

// RGB24toHSV.cpp
// 2023/10/09 by marsee
// 2023/10/15 : バグを修正
// 2023/10/17 : MAG を 8 ビットから 16 ビットに変更
//
// 入力フォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// 出力フォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define MAG 16 // 小数点以下のビット幅

#define ForAXIVdma  0
#define ForAXIDma   1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register

    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    int32_t r, g, b;
    int32_t h, s, v;
    int32_t max, min;
    int32_t hsv;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

    loop_y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        loop_x: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
            if(!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


テストベンチの RGB24toHSV_tb.cpp を示す。

// RGB24toHSV_tb.cpp
// 2023/10/09 by marsee
// 2023/10/15 : バグを修正、RGB値、HSV値をテキスト・ファイルに出力した。
// 2023/10/17 : MAG を 8 ビットから 16 ビットに変更、RGB24toHSV_soft() を浮動小数点数で実装
//
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

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

#include "bmp_header.h"

#define ForAXIVdma  0
#define ForAXIDma   1

#define BMP_FILE_NAME   "test2.bmp"
#define SQUARE_ERROR_LIMIT    1 // 2乗誤差のエラー限界、この数より上はエラーとする

#define H    0
#define S    1
#define V    2

#define HSV_TEXT    0
#define RGB_TEXT    1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv);
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB);

int main(){
    using namespace std;

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

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // BMP ファイル をオープン
        fprintf(stderr, "Can't open test2.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 =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_hsv memory\n");
        exit(1);
    }
    if ((sw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_hsv 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;
        ins_soft << 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;
        }
    }

    RGB24toHSV(ins, outs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    RGB24toHSV_soft(ins_soft, outs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    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_hsv[(j*bmpihr.biWidth)+i] = (int)val;
            sw_hsv[(j*bmpihr.biWidth)+i] = (int)val_soft;

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > SQUARE_ERROR_LIMIT || // v の2乗誤差が4よりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > SQUARE_ERROR_LIMIT || // s の2乗誤差が4よりも大きい
                (double)pow((double)((val>>16)&0x1ff)-((val_soft>>16)&0x1ff),(double)2) > SQUARE_ERROR_LIMIT){ // h の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_h = %d, HW_s = %d, HW_v = %d, SW_h = %d, SW_s = %d, SW_v = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("h_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, H);

    if ((fbmpw=fopen("s_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, S);

    if ((fbmpw=fopen("v_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, V);

    if ((fbmpw=fopen("h_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, H);

    if ((fbmpw=fopen("s_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, S);

    if ((fbmpw=fopen("v_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, V);

    if ((fbmpw=fopen("rgb_text.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open rgb_text.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, rd_bmp, RGB_TEXT);

    if ((fbmpw=fopen("hsv_text.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open hsv_text.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, hw_hsv, HSV_TEXT);

    free(rd_bmp);
    free(hw_hsv);
    free(sw_hsv);


}

// float で計算を行う
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    int32_t r, g, b;
    int32_t h, s, v;
    float ht, st;
    float max, min;
    int32_t hsv;

    do{
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = (float)r; // 8倍
                min = (float)r;
            }else if(r>=g && r>=b){ // r が最大
                max = (float)r;
                if(g>=b)
                    min = (float)b;
                else
                    min = (float)g;
            }else if(g>=r && g>=b){ // g が最大
                max = (float)g;
                if(r>=b)
                    min = (float)b;
                else
                    min = (float)r;
            }else{ // b が最大
                max = (float)b;
                if(r>=g)
                    min = (float)g;
                else
                    min = (float)r;
            }
            if(max-min == 0.0)
                ht = 0.0;
            else if(max == (float)r)
                ht = 60.0 * (((float)g-(float)b)/(max-min));
            else if(max == (float)g)
                ht = 60.0 * (((float)b-(float)r)/(max-min)) + 120.0; // MAGビットシフトして精度を確保
            else // if(max == b)
                ht = 60.0 * (((float)r-(float)g)/(max-min)) + 240.0; // MAGビットシフトして精度を確保

            if(ht < 0)
                ht += 360.0;
            h = (uint32_t)(ht + 0.5);

            if(max == 0.0)
                st = 0.0;
            else
                st = ((max - min)/max) * 255.0; // MAGビットシフトして精度を確保
            s = (uint32_t)(st + 0.5);

            v = (uint32_t)max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


// SV のうちの1つをblue, green, redの値として同じ値をBMPファイルに書く
// H は S,V の値を255とした時の H の値に対する RGB の値を計算する
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv){
    uint32_t h;
    uint32_t sv;
    uint32_t r, g, b;

    // 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++){
            switch(select_hsv){
            case H:
                h = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0x1ff;
                if(h>=0 && h<60){
                    r = 255;
                    g = (int)(((float)h/60.0)*255.0+0.5);
                    b = 0;
                }else if(h>=60 && h<120){
                    r = (int)(((120.0-(float)h)/60.0)*255+0.5);
                    g = 255;
                    b = 0;
                }else if(h>=120 && h<180){
                    r = 0;
                    g = 255;
                    b = (int)((((float)h-120.0)/60.0)*255+0.5);
                }else if(h>=180 && h<240){
                    r = 0;
                    g = (int)(((240.0-(float)h)/60.0)*255+0.5);
                    b = 255;
                }else if(h>=240 && h<300){
                    r = (int)((((float)h-240.0)/60.0)*255+0.5);
                    g = 0;
                    b = 255;
                }else{ // h>=300 && h<=360
                    r = 255;
                    g = 0;
                    b = (int)(((360.0-(float)h)/60.0)*255+0.5);
                }
                break;
            case S:
                sv = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                break;
            default: // case V:
                sv = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                break;
            }

            if(select_hsv==S || select_hsv==V){
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
            }else{
                fputc(b, fbmpw);
                fputc(g, fbmpw);
                fputc(r, fbmpw);
            }
        }
    }
    fclose(fbmpw);
}

// rgb, hsv をテキストファイルに書き込む
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB){
    uint32_t hr, sg, vb;

    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            if (HSVorRGB == HSV_TEXT)
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0x1ff;
            else
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0xff;
            sg = (pixel_buf[y*bmpihr.biWidth+x] >> 8) & 0xff;
            vb = pixel_buf[y*bmpihr.biWidth+x] & 0xff;
            fprintf(fbmpw, "%d, %d, %d\n",hr, sg, vb);
        }
    }
    fclose(fbmpw);

    return(0);
}


solution3 を作成し、C シミュレーションを行った。結果を示す。
zub1cg_i5filters_46_231020.png

C コードの合成を行った。
前回は、FF が 5310 個、LUT が 4324 個だったが、今回は、FF が 9197 個、LUT が 6950 個で増えていた。全体のビット長は変えずに、小数点以下を変えただけなのに、増え方が激しい気がする。
zub1cg_i5filters_47_231020.png

Export RTL、Implementation を行った。
solution2 と solution3 の比較を示す。やはり、solution3 の方がリソース使用量が大きい。
zub1cg_i5filters_48_231020.png
zub1cg_i5filters_49_231020.png
  1. 2023年10月20日 04:58 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する4

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する3”の続き。

前回は、バグを見つけ、コードを修正して、ほとんど元画像に戻すことができるようなった。また、元画像と RGB - HSV - RGB 変換した画像を元画像の 2 乗誤差を 4 以内に収めるには、RGB - HSV 変換 IP と HSV - RGB 変換 IP の固定小数点数の小数点以下を 16 桁にする必要があった。C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

C コードの合成を行った。結果を示す。
zub1cg_i5filters_40_231019.png
zub1cg_i5filters_41_231019.png

レイテンシも問題無さそうだ。

C/RTL 強調シミュレーションを行った。結果を示す。
ここでもレイテンシは問題無さそうだ。
zub1cg_i5filters_42_231019.png

C/RTL 協調シミュレーションの全体波形を示す。
TVALID 、TREADY がほとんど 1 で問題無さそうだ。
zub1cg_i5filters_43_231019.png

Export RTLを行った。

Implementation を行った。結果を示す。
zub1cg_i5filters_44_231019.png
zub1cg_i5filters_45_231019.png

CP achieved post-implementation も成約が 10 ns に対して 7.522 ns で問題無さそうだ。
  1. 2023年10月19日 05:04 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する3

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する2”の続き。

前回は、C シミュレーションを行った。RGB の画像を RGB色空間から HSV 色空間に変換して、HSV から RGB に変換したところ元画像に戻らなかった。今回は、バグを見つけ、コードを修正して、ほとんど元画像に戻すことができるようなった。また、元画像と RGB - HSV - RGB 変換した画像を元画像の 2 乗誤差を 4 以内に収めるには、RGB - HSV 変換 IP と HSV - RGB 変換 IP の固定小数点数の小数点以下を 16 桁にする必要があった。

バグを修正し、任意精度固定小数点数の少数以下を 16 桁にしたソースコードの HSVtoRGB24.cpp を示す。
(2023/11/09:追記) col_size のプラグマが axis になっていたので、s_axilie に変更した。C コードの合成結果や Implementation の結果は”ZUBoard 1CG の PYNQ v3.0.1 で自作の 5 個のフィルタを動作させる2”を参照のこと。

// HSVtoRGB24.cpp
// 2023/10/12 by marsee
// 2023/10/16 : バグをフィックスした
//
// 入力フォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// 出力フォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define ForAXIVdma  0
#define ForAXIDma   1

int HSVtoRGB24(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=s_axilite port=col_size register
#pragma HLS INTERFACE mode=s_axilite port=row_size register
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register

    ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT> ht, st, vt;
    ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT> max, min;
    uint8_t r, g, b;
    uint32_t h, s, v;
    ap_axiu<32,1,1,1> hsve;
    ap_axiu<24,1,1,1> rgbe;
    ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT> ap_0_5, ap_60, ap_120, ap_240, ap_360;

    ap_0_5 = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)0.5;
    ap_60 = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)60;
    ap_120 = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)120;
    ap_240 = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)240;
    ap_360 = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)360;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> hsve;
        if(function == ForAXIDma)
            break;
    }while(hsve.user == 0);

    loop_y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        loop_x: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
            if(!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> hsve;    // AXI4-Stream からの入力

            h = (hsve.data >> 16) & 0x1ff;
            s = (hsve.data >> 8) & 0xff;
            v = hsve.data & 0xff;
            ht = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)h;
            st = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)s;
            vt = (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)v;

            max = vt;
            min = max - (st / (ap_ufixed<32,16, AP_TRN_ZERO, AP_SAT>)255) * max;

            if(h>=0 && h<60){
                r = (uint8_t)max;
                g = (uint8_t)((ht / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=60 && h<120){
                r = (uint8_t)(((ap_120 - ht) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)max;
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=120 && h<180){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)max;
                b = (uint8_t)(((ht - ap_120) / ap_60) * (max - min) + min + ap_0_5);
            }else if(h>=180 && h<240){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(((ap_240 - ht) / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)max;
            }else if(h>=240 && h<300){
                r = (uint8_t)(((ht - ap_240) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)max;
            }else{ // h>=300 && h<=360
                r = (uint8_t)max;
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(((ap_360 - ht) / ap_60) * (max - min) + min + ap_0_5);
            }
            rgbe.data = (r << 16) + (g << 8) + b;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    rgbe.user = 1;
                else
                    rgbe.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    rgbe.last = 1;
                else
                    rgbe.last = 0;
            } else {
                rgbe.user = 0;
                rgbe.last = hsve.last;
            }
            rgbe.keep = 0xf;
            rgbe.strb = 0xf;

            outs << rgbe;
        }
    }

    return(0);
}


テストベンチの HSVtoRGB24_tb.cpp を示す。

// HSVtoRGB24_tb.cpp
// 2023/10/12 by marsee
// 2023/10/16 : バグをフィックスした。原画像のデータのテキストとRGB-HSV 、HSV-RGB 変換の結果をテキストで表示
//
// HSVのフォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// RGBのフォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

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

#include "bmp_header.h"

#define ForAXIVdma  0
#define ForAXIDma   1

#define MAG 16 // 小数点以下のビット幅

#define BMP_FILE_NAME   "test2.bmp"
#define HS_SQUARE_ERROR_LIMIT    4 // 2乗誤差のエラー限界、この数以上はエラーとする
#define ORG_SQUARE_ERROR_LIMIT    4
// 2乗誤差のエラー限界、この数以上はエラーとする

#define HSV_TEXT    0
#define RGB_TEXT    1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf);
int HSVtoRGB24(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int HSVtoRGB24_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB);

int main(){
    using namespace std;

    hls::stream<ap_axiu<24,1,1,1> > ins;
    hls::stream<ap_axiu<24,1,1,1> > ins_soft;
    hls::stream<ap_axiu<32,1,1,1> > hsvs;
    hls::stream<ap_axiu<32,1,1,1> > hsvs_soft;
    hls::stream<ap_axiu<24,1,1,1> > outs;
    hls::stream<ap_axiu<24,1,1,1> > outs_soft;
    ap_axiu<24,1,1,1> pix;
    ap_axiu<24,1,1,1> vals;
    ap_axiu<24,1,1,1> vals_soft;

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // BMP ファイル をオープン
        fprintf(stderr, "Can't open test2.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 =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_rgb =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_rgb memory\n");
        exit(1);
    }
    if ((sw_rgb =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_rgb 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;
        ins_soft << 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;
        }
    }

    RGB24toHSV_soft(ins, hsvs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    RGB24toHSV_soft(ins_soft, hsvs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    HSVtoRGB24(hsvs, outs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    HSVtoRGB24_soft(hsvs_soft, outs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    // ハードウェアとソフトウェアの RGB - HSV - RGB の値を比較する
    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_int<24> val = vals.data;
            ap_int<24> val_soft = vals_soft.data;

            hw_rgb[(j*bmpihr.biWidth)+i] = (int)val;
            sw_rgb[(j*bmpihr.biWidth)+i] = (int)val_soft;

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > HS_SQUARE_ERROR_LIMIT || // B の2乗誤差がSQUARE_ERROR_LIMITよりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > HS_SQUARE_ERROR_LIMIT || // G の2乗誤差がSQUARE_ERROR_LIMITよりも大きい
                (double)pow((double)((val>>16)&0xff)-((val_soft>>16)&0xff),(double)2) > HS_SQUARE_ERROR_LIMIT){ // R の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_r = %d, HW_g = %d, HW_b = %d, SW_r = %d, SW_g = %d, SW_b = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }

            if ((double)pow((double)(val&0xff)-blue,(double)2) > ORG_SQUARE_ERROR_LIMIT || // B の2乗誤差がSQUARE_ERROR_LIMITよりも大きい
                (double)pow((double)((val>>8)&0xff)-green,(double)2) > ORG_SQUARE_ERROR_LIMIT || // G の2乗誤差がSQUARE_ERROR_LIMITよりも大きい
                (double)pow((double)((val>>16)&0xff)-red,(double)2) > ORG_SQUARE_ERROR_LIMIT){ // R の2乗誤差がSQUARE_ERROR_LIMITよりも大きい
                printf("ERROR original BMP data and RGB-HSV-RGB data mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_r = %d, HW_g = %d, HW_b = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("hw_rgb.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open hw_rgb.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_rgb);

    if ((fbmpw=fopen("sw_rgb.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open sw_rgb.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_rgb);

    if ((fbmpw=fopen("org_rgb.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open org_rgb.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, rd_bmp, RGB_TEXT);

    if ((fbmpw=fopen("rgb_text.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open rgb_text.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, hw_rgb, RGB_TEXT);

    free(rd_bmp);
    free(hw_rgb);
    free(sw_rgb);

}

int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    int32_t r, g, b;
    int32_t h, s, v;
    int32_t max, min;
    int32_t hsv;

    do{
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}

// pixel_buf の画像をファイルに書き込む
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf){
    uint32_t h;
    uint32_t sv;
    uint32_t r, g, b;

    // 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++){
            uint32_t rgb_data = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x];
            fputc(rgb_data & 0xff, fbmpw);          // blue;
            fputc((rgb_data >> 8) & 0xff, fbmpw);   // green
            fputc((rgb_data >> 16) & 0xff, fbmpw);  // red
        }
    }
    fclose(fbmpw);
}

int HSVtoRGB24_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){

    ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT> ht, st, vt;
    ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT> max, min;
    uint8_t r, g, b;
    uint32_t h;
    ap_axiu<32,1,1,1> hsve;
    ap_axiu<24,1,1,1> rgbe;
    ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT> ap_0_5, ap_60, ap_120, ap_240, ap_360;

    ap_0_5 = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)0.5;
    ap_60 = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)60;
    ap_120 = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)120;
    ap_240 = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)240;
    ap_360 = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)360;

    do{
        ins >> hsve;
        if(function == ForAXIDma)
            break;
    }while(hsve.user == 0);

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

            h = (hsve.data >> 16) & 0x1ff;
            ht = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 16) & 0x1ff);
            st = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 8) & 0xff);
            vt = (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)(hsve.data & 0xff);

            max = vt;
            min = max - (st / (ap_ufixed<32, 16, AP_TRN_ZERO, AP_SAT>)255) * max;

            if(h>=0 && h<60){
                r = (uint8_t)max;
                g = (uint8_t)((ht / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=60 && h<120){
                r = (uint8_t)(((ap_120 - ht) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)max;
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=120 && h<180){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)max;
                b = (uint8_t)(((ht - ap_120) / ap_60) * (max - min) + min + ap_0_5);
            }else if(h>=180 && h<240){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(((ap_240 - ht) / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)max;
            }else if(h>=240 && h<300){
                r = (uint8_t)(((ht - ap_240) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)max;
            }else{ // h>=300 && h<=360
                r = (uint8_t)max;
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(((ap_360 - ht) / ap_60) * (max - min) + min + ap_0_5);
            }
            rgbe.data = (r << 16) + (g << 8) + b;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    rgbe.user = 1;
                else
                    rgbe.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    rgbe.last = 1;
                else
                    rgbe.last = 0;
            } else {
                rgbe.user = 0;
                rgbe.last = hsve.last;
            }
            rgbe.keep = 0xf;
            rgbe.strb = 0xf;

            outs << rgbe;
        }
    }

    return(0);
}

// rgb, hsv をテキストファイルに書き込む
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB){
    uint32_t hr, sg, vb;

    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            if (HSVorRGB == HSV_TEXT)
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0x1ff;
            else
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0xff;
            sg = (pixel_buf[y*bmpihr.biWidth+x] >> 8) & 0xff;
            vb = pixel_buf[y*bmpihr.biWidth+x] & 0xff;
            fprintf(fbmpw, "%d, %d, %d\n",hr, sg, vb);
        }
    }
    fclose(fbmpw);

    return(0);
}


C シミュレーションを行った。結果を示す。
成功だ。元画像と RGB - HSV - RGB 変換した画像の 2 乗誤差は 4 (つまり差分は 2 以下)に収まっている。
zub1cg_i5filters_36_231018.png

solution2 を作成して、作業を行っている。
solution2/csim/build ディレクトリを示す。
zub1cg_i5filters_37_231018.png

hw_rgb.bmp と sw_rgb.bmp 共に元画像に戻っているようだ。
zub1cg_i5filters_38_231018.png

元画像のテキスト・データの org_rgb.txt と RGB - HSV - RGB 変換した画像のテキスト・データを比較した。
zub1cg_i5filters_39_231018.png
  1. 2023年10月18日 04:58 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する6

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する5”の続き。

前回は、HSV - RGB 変換 IP の C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

C コードの合成を行った。結果を示す。
zub1cg_i5filters_30_231016.png
zub1cg_i5filters_31_231016.png

1920 x 1080 = 2073600 ピクセルを 2073631 クロックで RGB - HSV 変換できるようだ。

C/RT 協調シミュレーション結果を示す。
800 x 600 = 480000 ピクセルを 480033 クロックで処理できる。
zub1cg_i5filters_32_231016.png

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

Export RTL を行った。

Implementation を行った。結果を示す。
zub1cg_i5filters_34_231016.png
zub1cg_i5filters_35_231016.png

問題無さそうだ。
  1. 2023年10月17日 04:48 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する5

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する4”の続き。

前回は、バグがあったので、RGB-HSV 変換 IP から検証したところ、バグが見つかった。バグを修正したソースコードとテストベンチを示した。今回は、C シミュレーションを行った。

solution2 を新規作成して、C シミュレーションを行った。結果を示す。
zub1cg_i5filters_24_231014.png

solution2/csim/build ディレクトリを示す。
h_hw.bmp がハードウェア用の HSV 変換の H の結果を 255 までに圧縮した画像で、h_sw.bmp はそのソフトウェア版だ。C シミュレーションでは 2 つは同じ画像になるはず。
s_hw.bmp と s_sw.bmp は S の値をグレーの画像に変換した画像となる。
v_hw.bmp と v_sw.bmp は V の値をグレーの画像に変換した画像となる。
zub1cg_i5filters_25_231014.png

h_hw.bmp と h_sw.bmp を示す。
zub1cg_i5filters_26_231014.jpg

s_hw.bmp と s_sw.bmp を示す。
zub1cg_i5filters_27_231014.jpg

v_hw.bmp と v_sw.bmp を示す。
zub1cg_i5filters_28_231014.jpg

HSV の値を書き込んだ hsv_text.txt ファイルと原画像の RGB の値を書き込んだ rgb_text.txt ファイルを示す。
zub1cg_i5filters_29_231014.png
  1. 2023年10月16日 03:59 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する4

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する3”の続き。

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する2”で、RGB-HSV 変換し、HSV-RGB 変換して元の画像に戻らなかったので、RGB-HSV 変換 IP と HSV-RGB 変換 IP をもう一度検証してみることにした。まずは、RGB-HSV 変換 IP から検証してみよう。

(2023/10/20 :追記)ソースコードとテストベンチを変更した。詳しくは”Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する7”参照。

RGB-HSV 変換 IP は”Vivado HLS でRGB2HSV IPを作る4(C ソースコード)”で一度書いたことがある。それと比べてみると、int で定義してある変数を uint32_t で定義してしまっている
RGBとHSV・HSBの相互変換ツールと変換計算式”を見ると h はマイナスになることもあるので、unsigned 型はまずい。ここにバグあったようだ。
ついでにテストベンチも原画像の RGB 値と HSV 変換後の HSV 値をテキスト・ファイルに出力するようにコードを変更した。
バグがあるコードだと、H が 500 以上になるときがある。正しくは、359 までのはずだ。
zub1cg_i5filters_21_231014.png

変数を uint32_t から int32_t に変更すると、H の値は 360 以下になった。
zub1cg_i5filters_23_231014.png

修正したソースファイルの RGB24toHSV.cpp を示す。

// RGB24toHSV.cpp
// 2023/10/09 by marsee
// 2023/10/15 : バグを修正
//
// 入力フォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// 出力フォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define MAG 8 // 小数点以下のビット幅

#define ForAXIVdma  0
#define ForAXIDma   1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register

    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    int32_t r, g, b;
    int32_t h, s, v;
    int32_t max, min;
    int32_t hsv;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

    loop_y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        loop_x: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
            if(!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


テストベンチの RGB24toHSV_tb.cpp を示す。

// RGB24toHSV_tb.cpp
// 2023/10/09 by marsee
// 2023/10/15 : バグを修正、RGB値、HSV値をテキスト・ファイルに出力した。
//
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

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

#include "bmp_header.h"

#define ForAXIVdma  0
#define ForAXIDma   1

#define MAG 8 // 小数点以下のビット幅

#define BMP_FILE_NAME   "test2.bmp"
#define SQUARE_ERROR_LIMIT    4 // 2乗誤差のエラー限界、この数以上はエラーとする

#define H    0
#define S    1
#define V    2

#define HSV_TEXT    0
#define RGB_TEXT    1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv);
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB);

int main(){
    using namespace std;

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

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // BMP ファイル をオープン
        fprintf(stderr, "Can't open test2.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 =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_hsv memory\n");
        exit(1);
    }
    if ((sw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_hsv 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;
        ins_soft << 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;
        }
    }

    RGB24toHSV(ins, outs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    RGB24toHSV_soft(ins_soft, outs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    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_hsv[(j*bmpihr.biWidth)+i] = (int)val;
            sw_hsv[(j*bmpihr.biWidth)+i] = (int)val_soft;

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > SQUARE_ERROR_LIMIT || // v の2乗誤差が4よりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > SQUARE_ERROR_LIMIT || // s の2乗誤差が4よりも大きい
                (double)pow((double)((val>>16)&0x1ff)-((val_soft>>16)&0x1ff),(double)2) > SQUARE_ERROR_LIMIT){ // h の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_h = %d, HW_s = %d, HW_v = %d, SW_h = %d, SW_s = %d, SW_v = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("h_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, H);

    if ((fbmpw=fopen("s_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, S);

    if ((fbmpw=fopen("v_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, V);

    if ((fbmpw=fopen("h_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, H);

    if ((fbmpw=fopen("s_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, S);

    if ((fbmpw=fopen("v_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, V);

    if ((fbmpw=fopen("rgb_text.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open rgb_text.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, rd_bmp, RGB_TEXT);

    if ((fbmpw=fopen("hsv_text.txt", "wt")) == NULL){
        fprintf(stderr, "Can't open hsv_text.txt by binary write mode\n");
        exit(1);
    }
    WriteTextFile(fbmpw, bmpfhr, bmpihr, hw_hsv, HSV_TEXT);

    free(rd_bmp);
    free(hw_hsv);
    free(sw_hsv);


}

int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    int32_t r, g, b;
    int32_t h, s, v;
    int32_t max, min;
    int32_t hsv;

    do{
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


// SV のうちの1つをblue, green, redの値として同じ値をBMPファイルに書く
// H は S,V の値を255とした時の H の値に対する RGB の値を計算する
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv){
    uint32_t h;
    uint32_t sv;
    uint32_t r, g, b;

    // 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++){
            switch(select_hsv){
            case H:
                h = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0x1ff;
                if(h>=0 && h<60){
                    r = 255;
                    g = (int)(((float)h/60.0)*255.0+0.5);
                    b = 0;
                }else if(h>=60 && h<120){
                    r = (int)(((120.0-(float)h)/60.0)*255+0.5);
                    g = 255;
                    b = 0;
                }else if(h>=120 && h<180){
                    r = 0;
                    g = 255;
                    b = (int)((((float)h-120.0)/60.0)*255+0.5);
                }else if(h>=180 && h<240){
                    r = 0;
                    g = (int)(((240.0-(float)h)/60.0)*255+0.5);
                    b = 255;
                }else if(h>=240 && h<300){
                    r = (int)((((float)h-240.0)/60.0)*255+0.5);
                    g = 0;
                    b = 255;
                }else{ // h>=300 && h<=360
                    r = 255;
                    g = 0;
                    b = (int)(((360.0-(float)h)/60.0)*255+0.5);
                }
                break;
            case S:
                sv = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                break;
            default: // case V:
                sv = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                break;
            }

            if(select_hsv==S || select_hsv==V){
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
            }else{
                fputc(b, fbmpw);
                fputc(g, fbmpw);
                fputc(r, fbmpw);
            }
        }
    }
    fclose(fbmpw);
}

// rgb, hsv をテキストファイルに書き込む
int WriteTextFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t HSVorRGB){
    uint32_t hr, sg, vb;

    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            if (HSVorRGB == HSV_TEXT)
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0x1ff;
            else
                hr = (pixel_buf[y*bmpihr.biWidth+x]>>16) & 0xff;
            sg = (pixel_buf[y*bmpihr.biWidth+x] >> 8) & 0xff;
            vb = pixel_buf[y*bmpihr.biWidth+x] & 0xff;
            fprintf(fbmpw, "%d, %d, %d\n",hr, sg, vb);
        }
    }
    fclose(fbmpw);

    return(0);
}

  1. 2023年10月15日 07:16 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する2

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する1”の続き。

Vitis HLS 2023.1 で RGB 色空間を HSV 色空間に変換する IP を作ってきたが、HSV 色空間を RGB 色空間に変換する HSVtoRGB24 IP を作ってみよう。
前回は、ソースコード、テストベンチを貼って、Vitis HLS 2023.1 の HSVtoRGB24 プロジェクトを作成した。今回は、C シミュレーションを行った。RGB の画像を RGB色空間から HSV 色空間に変換して、HSV から RGB に変換したところ元画像に戻らなかった。

C シミュレーションを行った。
zub1cg_i5filters_18_231013.png

HSVtoRGB24/solution1/csim/build ディレクトリを示す。
zub1cg_i5filters_19_231013.png

ハードウェアの hw_rgb.bmp とソフトウェアの sw_rgb.bmp を示す。元画像に戻っていない。
zub1cg_i5filters_20_231013.jpg

デバッグすることにする。
  1. 2023年10月14日 04:20 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する1

Vitis HLS 2023.1 で RGB 色空間を HSV 色空間に変換する IP を作ってきたが、HSV 色空間を RGB 色空間に変換する HSVtoRGB24 IP を作ってみよう。

(2023/10/18:追記)ソースコードが間違っていたので、修正した。また、任意精度固定小数点数の少数の桁を 16 桁に変更した。詳しくは、”Vitis HLS 2023.1 で HSV を RGB に変換する IP を作成する3”を参照。

今回は、ソースコードとテストベンチを示して、Vitis HLS 2023.1 の HSVtoRGB24 プロジェクトを示す。

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;


ソースコードの HSVtoRGB24.cpp を示す。

// HSVtoRGB24.cpp
// 2023/10/12 by marsee
//
// 入力フォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// 出力フォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define MAG 8 // 小数点以下のビット幅

#define ForAXIVdma  0
#define ForAXIDma   1

int HSVtoRGB24(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=axis register_mode=both port=col_size register
#pragma HLS INTERFACE mode=s_axilite port=row_size register
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register

    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> ht, st, vt;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> max, min;
    uint8_t r, g, b;
    uint32_t h;
    ap_axiu<32,1,1,1> hsve;
    ap_axiu<24,1,1,1> rgbe;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> ap_0_5, ap_60, ap_120, ap_240, ap_360;

    ap_0_5 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)0.5;
    ap_60 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)60;
    ap_120 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)120;
    ap_240 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)240;
    ap_360 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)360;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> hsve;
        if(function == ForAXIDma)
            break;
    }while(hsve.user == 0);

    loop_y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        loop_x: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
            if(!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> hsve;    // AXI4-Stream からの入力

            h = (hsve.data >> 16) & 0x1ff;
            ht = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 16) & 0x1ff);
            st = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 8) & 0xff);
            vt = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)(hsve.data & 0xff);

            max = vt;
            min = max - (st / (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)255) * max;

            if(h>=0 && h<60){
                r = (uint8_t)(max + ap_0_5);
                g = (uint8_t)(ht * (max - min) + min + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=60 && h<120){
                r = (uint8_t)(((ap_120 - ht) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(max + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=120 && h<180){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(max + ap_0_5);
                b = (uint8_t)((ht - ap_120) * (max - min) + min + ap_0_5);
            }else if(h>=180 && h<240){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(((ap_240 - ht) / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)(max + ap_0_5);
            }else if(h>=240 && h<300){
                r = (uint8_t)(((ht - ap_240) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(max + ap_0_5);
            }else{ // h>=300 && h<=360
                r = (uint8_t)(max + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(((ap_360 - ht) / ap_60) * (max - min) + min + ap_0_5);
            }
            rgbe.data = (r << 16) + (g << 8) + b;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    rgbe.user = 1;
                else
                    rgbe.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    rgbe.last = 1;
                else
                    rgbe.last = 0;
            } else {
                rgbe.user = 0;
                rgbe.last = hsve.last;
            }
            rgbe.keep = 0xf;
            rgbe.strb = 0xf;

            outs << rgbe;
        }
    }

    return(0);
}


テストベンチの HSVtoRGB24_tb.cpp を示す。
このテストベンチは、BMP ファイルを一度 HSV 変換してから HSV - RGB 変換して、BMP ファイルとして出力するテストベンチである。

// HSVtoRGB24_tb.cpp
// 2023/10/12 by marsee
//
// HSVのフォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// RGBのフォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

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

#include "bmp_header.h"

#define ForAXIVdma  0
#define ForAXIDma   1

#define MAG 8 // 小数点以下のビット幅

#define BMP_FILE_NAME   "test2.bmp"
#define SQUARE_ERROR_LIMIT    4 // 2乗誤差のエラー限界、この数以上はエラーとする

#define H    0
#define S    1
#define V    2

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf);
int HSVtoRGB24(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int HSVtoRGB24_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int main(){
    using namespace std;

    hls::stream<ap_axiu<24,1,1,1> > ins;
    hls::stream<ap_axiu<24,1,1,1> > ins_soft;
    hls::stream<ap_axiu<32,1,1,1> > hsvs;
    hls::stream<ap_axiu<32,1,1,1> > hsvs_soft;
    hls::stream<ap_axiu<24,1,1,1> > outs;
    hls::stream<ap_axiu<24,1,1,1> > outs_soft;
    ap_axiu<24,1,1,1> pix;
    ap_axiu<24,1,1,1> vals;
    ap_axiu<24,1,1,1> vals_soft;

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // BMP ファイル をオープン
        fprintf(stderr, "Can't open test2.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 =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_rgb =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_rgb memory\n");
        exit(1);
    }
    if ((sw_rgb =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_rgb 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;
        ins_soft << 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;
        }
    }

    RGB24toHSV_soft(ins, hsvs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    RGB24toHSV_soft(ins_soft, hsvs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    HSVtoRGB24(hsvs, outs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    HSVtoRGB24_soft(hsvs_soft, outs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    // ハードウェアとソフトウェアの RGB - HSV - RGB の値を比較する
    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_int<24> val = vals.data;
            ap_int<24> val_soft = vals_soft.data;

            hw_rgb[(j*bmpihr.biWidth)+i] = (int)val;
            sw_rgb[(j*bmpihr.biWidth)+i] = (int)val_soft;

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > SQUARE_ERROR_LIMIT || // B の2乗誤差が4よりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > SQUARE_ERROR_LIMIT || // G の2乗誤差が4よりも大きい
                (double)pow((double)((val>>16)&0xff)-((val_soft>>16)&0xff),(double)2) > SQUARE_ERROR_LIMIT){ // R の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_h = %d, HW_s = %d, HW_v = %d, SW_h = %d, SW_s = %d, SW_v = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("hw_rgb.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open hw_rgb.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_rgb);

    if ((fbmpw=fopen("sw_rgb.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open sw_rgb.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_rgb);

    free(rd_bmp);
    free(hw_rgb);
    free(sw_rgb);

}

int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    uint32_t r, g, b;
    uint32_t h, s, v;
    uint32_t max, min;
    uint32_t hsv;

    do{
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}

// pixel_buf の画像をファイルに書き込む
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf){
    uint32_t h;
    uint32_t sv;
    uint32_t r, g, b;

    // 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++){
            uint32_t rgb_data = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x];
            fputc(rgb_data & 0xff, fbmpw);          // blue;
            fputc((rgb_data >> 8) & 0xff, fbmpw);   // green
            fputc((rgb_data >> 16) & 0xff, fbmpw);  // red
        }
    }
    fclose(fbmpw);
}

int HSVtoRGB24_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, hls::stream<ap_axiu<24,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){

    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> ht, st, vt;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> max, min;
    uint8_t r, g, b;
    uint32_t h;
    ap_axiu<32,1,1,1> hsve;
    ap_axiu<24,1,1,1> rgbe;
    ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT> ap_0_5, ap_60, ap_120, ap_240, ap_360;

    ap_0_5 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)0.5;
    ap_60 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)60;
    ap_120 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)120;
    ap_240 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)240;
    ap_360 = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)360;

    do{
        ins >> hsve;
        if(function == ForAXIDma)
            break;
    }while(hsve.user == 0);

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

            h = (hsve.data >> 16) & 0x1ff;
            ht = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 16) & 0x1ff);
            st = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)((hsve.data >> 8) & 0xff);
            vt = (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)(hsve.data & 0xff);

            max = vt;
            min = max - (st / (ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)255) * max;

            if(h>=0 && h<60){
                r = (uint8_t)(max + ap_0_5);
                g = (uint8_t)(ht * (max - min) + min + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=60 && h<120){
                r = (uint8_t)(((ap_120 - ht) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(max + ap_0_5);
                b = (uint8_t)(min + ap_0_5);
            }else if(h>=120 && h<180){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(max + ap_0_5);
                b = (uint8_t)((ht - ap_120) * (max - min) + min + ap_0_5);
            }else if(h>=180 && h<240){
                r = (uint8_t)(min + ap_0_5);
                g = (uint8_t)(((ap_240 - ht) / ap_60) * (max - min) + min + ap_0_5);
                b = (uint8_t)(max + ap_0_5);
            }else if(h>=240 && h<300){
                r = (uint8_t)(((ht - ap_240) / ap_60) * (max - min) + min + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(max + ap_0_5);
            }else{ // h>=300 && h<=360
                r = (uint8_t)(max + ap_0_5);
                g = (uint8_t)(min + ap_0_5);
                b = (uint8_t)(((ap_360 - ht) / ap_60) * (max - min) + min + ap_0_5);
            }
            rgbe.data = (r << 16) + (g << 8) + b;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    rgbe.user = 1;
                else
                    rgbe.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    rgbe.last = 1;
                else
                    rgbe.last = 0;
            } else {
                rgbe.user = 0;
                rgbe.last = hsve.last;
            }
            rgbe.keep = 0xf;
            rgbe.strb = 0xf;

            outs << rgbe;
        }
    }

    return(0);
}


test2.bmp を示すが、BMP ファイルではなく JPEG ファイルを貼っておく。
zub1cg_i5filters_1_231010.jpg

Vitis HLS 2023.1 の HSVtoRGB24 プロジェクトを示す。
zub1cg_i5filters_17_231013.png
  1. 2023年10月13日 04:21 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する3

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する2”の続き。

前回は、RGB から HSV 色空間に変換する Vitis HLS 2023.1 プロジェクトの RGB24toHSV の C シミュレーションを行った。今回は、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

C コードの合成を行った。結果を示す。
ほぼ 1 クロックで 1 出力が出ている。
ループの Iteration Latency が 37 クロックもあるな。。。
zub1cg_i5filters_8_231012.png
zub1cg_i5filters_9_231012.png
zub1cg_i5filters_10_231012.png

C/RTL 協調シミュレーションを行った。結果を示す。
800 x 600 の画像を使用してるので、1 クロックで 1 ピクセル出力に 47 クロックだけ余計だ。
zub1cg_i5filters_11_231012.png

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

TVALID と TREADY がほとんど 1 なので、問題無さそうだ。

最初のレイテンシは 49 クロックのようだ。
zub1cg_i5filters_13_231012.png

最後のレイテンシは 38 クロックのようだ。これが、実際のレイテンシなのだろう?
zub1cg_i5filters_14_231012.png

Export RTL を行った。

Implementation を行った。結果を示す。
問題無さそうだ。
zub1cg_i5filters_15_231012.png
zub1cg_i5filters_16_231012.png
  1. 2023年10月12日 04:30 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する2

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する1”の続き。

前回は、RGB から HSV 色空間に変換する Vitis HLS 2023.1 プロジェクトの RGB24toHSV を作成し、ソースコードとテストベンチを貼った。今回は、C シミュレーションを行った。

まずは、テストベンチのコードは、一度 RGB24toHSV 関数で、RGB を HSV に変換する。
色相(H)の h_hw.bmp は、彩度(S)、明度(V)を 255 に固定して、HSV から RGB に変換している。
s_hw.bmp は 彩度(S)を BMP 画像にしている。明度(V)も同様だ。
詳細については、”Vivado HLS でRGB2HSV IPを作る3(H の表現方法)”を参照のこと。

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

Vitis_HLS/ZUBoard_1CG/2023.1/RGB24toHSV/solution1/csim/build ディレクトリを示す。
zub1cg_i5filters_4_231011.png

h_hw.bmp, s_hw.bmp, v_hw.bmp はハードウェアになる RGB24toHSV 関数が出力した HSV の値で、h_sw.bmp, s_sw.bmp, v_sw.bmp がソフトウェアの関数 RGB24toHSV_soft 関数が出力した HSV となっている。

色相の h_hw.bmp と h_sw.bmp を示す。
サイケデリックな画像になっている。ブロックになっているのは、元画像が JPEG を BMP に変換した画像だからかもしれない?
zub1cg_i5filters_5_231011.jpg

彩度の s_hw.bmp と s_sw.bmp を示す。
こちらもブロックになっている。
zub1cg_i5filters_6_231011.jpg

明度の v_hw.bmp と v_sw.bmp を示す。
明度の画像はブロックが見えない。
zub1cg_i5filters_7_231011.jpg
  1. 2023年10月11日 05:08 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する1

Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成しよう。ZUBoard 1CG 用とする。

RGB から HSV に変換する IP は、”Vivado HLS でRGB2HSV IPを作る4(C ソースコード)”で作成したことがあるが、このソースコードを元に Vitis HLS 用に row_size と col_size の変数を追加して HD 解像度までの任意の解像度に対応するように変更した。
また、入力の AXI4-Stream のデータ幅を 24 ビットに変更した。

(2023/10/15:追記)ソースコードとテストベンチが間違っています。詳細は、”Vitis HLS 2023.1 で RGB を HSV に変換する IP を作成する4”を参照ください。

最初に bmpheader.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;


ソースコードの RGB24toHSV.cpp を貼っておく。

// RGB24toHSV.cpp
// 2023/10/09 by marsee
//
// 入力フォーマット:23ビット〜16ビットーRED、15ビット〜8ビットーGREEN、7ビット〜0ビットーBLUE
// 出力フォーマット:24ビット〜16ビットー H、15ビット〜8ビットーS、7ビット〜0ビットーV
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define MAG 8 // 小数点以下のビット幅

#define ForAXIVdma  0
#define ForAXIDma   1

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register

    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    uint32_t r, g, b;
    uint32_t h, s, v;
    uint32_t max, min;
    uint32_t hsv;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

    loop_y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        loop_x: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
            if(!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


テストベンチの RGB24toHSV_tb.cpp を貼っておく。

// RGB24toHSV_tb.cpp
// 2023/10/09 by marsee
// ”RGBとHSV・HSBの相互変換ツールと変換計算式”を参考にしています (https://www.peko-step.com/tool/hsvrgb.html)

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

#include "bmp_header.h"

#define ForAXIVdma  0
#define ForAXIDma   1

#define MAG 8 // 小数点以下のビット幅

#define BMP_FILE_NAME   "test2.bmp"
#define SQUARE_ERROR_LIMIT    4 // 2乗誤差のエラー限界、この数以上はエラーとする

#define H    0
#define S    1
#define V    2

int RGB24toHSV(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv);

int main(){
    using namespace std;

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

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // BMP ファイル をオープン
        fprintf(stderr, "Can't open test2.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 =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_hsv memory\n");
        exit(1);
    }
    if ((sw_hsv =(uint32_t *)malloc(sizeof(uint32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_hsv 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;
        ins_soft << 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;
        }
    }

    RGB24toHSV(ins, outs, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);
    RGB24toHSV_soft(ins_soft, outs_soft, ForAXIVdma, bmpihr.biHeight, bmpihr.biWidth);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    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_hsv[(j*bmpihr.biWidth)+i] = (int)val;
            sw_hsv[(j*bmpihr.biWidth)+i] = (int)val_soft;

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > SQUARE_ERROR_LIMIT || // v の2乗誤差が4よりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > SQUARE_ERROR_LIMIT || // s の2乗誤差が4よりも大きい
                (double)pow((double)((val>>16)&0x1ff)-((val_soft>>16)&0x1ff),(double)2) > SQUARE_ERROR_LIMIT){ // h の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_h = %d, HW_s = %d, HW_v = %d, SW_h = %d, SW_s = %d, SW_v = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("h_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, H);

    if ((fbmpw=fopen("s_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, S);

    if ((fbmpw=fopen("v_hw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, V);

    if ((fbmpw=fopen("h_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open h_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, H);

    if ((fbmpw=fopen("s_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open s_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, S);

    if ((fbmpw=fopen("v_sw.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open v_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, V);

    free(rd_bmp);
    free(hw_hsv);
    free(sw_hsv);


}

int RGB24toHSV_soft(hls::stream<ap_axiu<24,1,1,1> >& ins, hls::stream<ap_axiu<32,1,1,1> >& outs,
        int32_t function, int32_t row_size, int32_t col_size){
    ap_axiu<24,1,1,1> pix;
    ap_axiu<32,1,1,1> hsvst;
    uint32_t r, g, b;
    uint32_t h, s, v;
    uint32_t max, min;
    uint32_t hsv;

    do{
        ins >> pix;
        if(function == ForAXIDma)
            break;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else{ // b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255; // MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = ((h&0x1ff)<<16) + ((s&0xff)<<8) + (v&0xff);

            hsvst.data = hsv;

            if(function == ForAXIVdma){
                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    hsvst.user = 1;
                else
                    hsvst.user = 0;
                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    hsvst.last = 1;
                else
                    hsvst.last = 0;
            } else {
                hsvst.user = 0;
                hsvst.last = pix.last;
            }
            hsvst.keep = 0xf;
            hsvst.strb = 0xf;

            outs << hsvst;
        }
    }

    return(0);
}


// SV のうちの1つをblue, green, redの値として同じ値をBMPファイルに書く
// H は S,V の値を255とした時の H の値に対する RGB の値を計算する
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, uint32_t *pixel_buf, uint32_t select_hsv){
    uint32_t h;
    uint32_t sv;
    uint32_t r, g, b;

    // 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++){
            switch(select_hsv){
            case H:
                h = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0x1ff;
                if(h>=0 && h<60){
                    r = 255;
                    g = (int)(((float)h/60.0)*255.0+0.5);
                    b = 0;
                }else if(h>=60 && h<120){
                    r = (int)(((120.0-(float)h)/60.0)*255+0.5);
                    g = 255;
                    b = 0;
                }else if(h>=120 && h<180){
                    r = 0;
                    g = 255;
                    b = (int)((((float)h-120.0)/60.0)*255+0.5);
                }else if(h>=180 && h<240){
                    r = 0;
                    g = (int)(((240.0-(float)h)/60.0)*255+0.5);
                    b = 255;
                }else if(h>=240 && h<300){
                    r = (int)((((float)h-240.0)/60.0)*255+0.5);
                    g = 0;
                    b = 255;
                }else{ // h>=300 && h<=360
                    r = 255;
                    g = 0;
                    b = (int)(((360.0-(float)h)/60.0)*255+0.5);
                }
                break;
            case S:
                sv = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                break;
            default: // case V:
                sv = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                break;
            }

            if(select_hsv==S || select_hsv==V){
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
            }else{
                fputc(b, fbmpw);
                fputc(g, fbmpw);
                fputc(r, fbmpw);
            }
        }
    }
    fclose(fbmpw);
}


test2.bmp を示すが、BMP ファイルではなく JPEG ファイルを貼っておく。
zub1cg_i5filters_1_231010.jpg

ZUBoard 1CG の FPGA の zu1cg-sbva484-1-e のデバイスを使用する Vitis HLS 2023.1 の RGB24toHSV プロジェクトを作成した。
zub1cg_i5filters_2_231010.png
  1. 2023年10月10日 04:44 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる13

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる12”の続き。

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる5”で作成した Vitis 2022.2 のアプリケーション・プロジェクトに C 言語のソースコードを書いて、PYNQ で動作させている回路で、動かしてみよう。

最初に、Micro SD カードを書き換える必要がある。
Vivado 2022.2 の i4filters プロジェクトのビット・ファイルが入った BOOT.BIN に入れ替える必要があるので、Micro SD カードの BOOT.BIN, boot.scr, image.ub を入れ替えた。
Micro SD カードの BOOT.BIN, boot.scr, image.ub を消去した。
ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters/images/linux の BOOT.BIN, boot.scr, image.ub を MicroSD カードにコピーした。
zub1cg_i4filters_92_231009.png

zub1cg_i4filters_93_231009.png

Micro SD カードを ZUBoard 1CG に入れて電源 ON + SW7 を押してブートした。
Petalinux 2022.2 が起動した。
ifconfig で IP アドレスを表示させた。
zub1cg_i4filters_94_231009.png

HDL/2023.1/zub1cg/i4filters/vitis_work ディレクトリの下に upload_files ディレクトリを作成し、HDL/2023.1/zub1cg/i4filters_peta/vitis_work/upload_files の test2.bmp, u-dma-buf.ko をコピーした。
zub1cg_i4filters_95_231009.png

FillZilla で test2.bmp, u-dma-buf.ko をホーム・ディレクトリにコピーした。
zub1cg_i4filters_96_231009.png

u-dma-buf.ko を insmod でロードした。
sudo insmod u-dma-buf.ko udmabuf0=2880000
zub1cg_i4filters_100_231009.png

なお、この場合のメモリ使用量は 800 ピクセル x 600 行 x 3 バイト x 2 = 2880000 バイトとなる。

Vitis 2022.2 の zub1cg_i4filters_system -> zub1cg_i4filters -> src に bmp_header.h と zub1cg_i4filters.c の 2 つのソースファイルを書いた。
トンカチ・ボタンをクリックして、ビルドを行ったところ、成功した。
zub1cg_i4filters_97_231009.png

Vitis 2022.2 の Explorer の zub1cg_i4filters_peta_system を右クリックし右クリックメニューから Debug As -> Debug Configurations... を選択した。

Debug Configurations ダイアログが開いた。
System Project Debug を右クリックし、右クリックメニューから New Configuration を選択した。

System Project Debug の下に SystemDebugger_zub1cg_i4filters_system が生成された。

Linux TCF Agent の New ボタンをクリックして、新しい TCF Agent のエントリを作成する。
Target Connection Details ダイアログが表示された。
Host には ZUBoard 1CG の Petalinux の IP アドレスを入力した。

Test Connection ボタンをクリックした。
connection successful! ダイアログが表示された。

OK ボタンをクリックして、Target Connection Details ダイアログを閉じた。
Debug Configurations ダイアログに戻った。

Remote Working Directory: に /home/petalinux を入力した。
zub1cg_i4filters_97_231009.png

Apply ボタンをクリックし、Debug ボタンをクリックすると、ソフトウェアが起動して、最初の行で止まっている。
zub1cg_i4filters_99_231009.png

Resume ボタンをクリックし、ソフトウェアを最後まで走らせた。
正常終了したようだ。
zub1cg_i4filters_101_231009.png

終了後、output_img.bmp ファイルが生成されていた。
zub1cg_i4filters_102_231009.png

output_img.bmp ファイルを FileZilla でパソコンにダウンロードした。
zub1cg_i4filters_103_231009.png

output_img.bmp ファイルはソーベル・フィルタ処理後の画像だった。
成功だ。。。
zub1cg_i4filters_104_231009.png

zub1cg_i4filters_105_231009.jpg

最後に zub1cg_i4filters.c のソースコードを貼っておく。

// zub1cg_i4filters.c
// 2023/10/04 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#include "bmp_header.h"

char INPUT_IMG_FILE[] = "test2.bmp";
char OUTPUT_IMG_FILE[] = "output_img.bmp";

#define BLOCK_SIZE      4096
#define IMAGE_WIDTH     800
#define IMAGE_HEIGHT    600
#define IMAGE_CHANNEL   3
#define MAT_IMGAGE_BUF  (IMAGE_WIDTH*IMAGE_HEIGHT*IMAGE_CHANNEL)

#define MM2S_CONTROL_REG        0x00
#define MM2S_STATUS_REG         (0x4 >> 2)
#define MM2S_START_ADDR         (0x18 >> 2)
#define MM2S_LENGTH_REG         (0x28 >> 2)
#define S2MM_CONTROL_REG        (0x30 >> 2)
#define S2MM_STATUS_REG         (0x34 >> 2)
#define S2MM_DESTINATION_ADDR   (0x48 >> 2)
#define S2MM_LENGTH_REG         (0x58 >> 2)
// bits 1 - idle
#define MM2S_IDLE_MASK  0x2
#define S2MM_IDLE_MASK  0x2

#define GUSSIAN_CONTROL     0x00
#define GUSSIAN_FUNCTON_R   (0x18 >> 2)
#define GUSSIAN_ROW_SIZE    (0x20 >> 2)
#define GUSSIAN_COL_SIZE     (0x28 >> 2)

#define MEDIAN_CONTROL      0x00
#define MEDIAN_FUNCTION_R   (0x18 >> 2)
#define MEDIAN_ROW_SIZE     (0x20 >> 2)
#define MEDIAN_COL_SIZE     (0x28 >> 2)

#define COLOR_CONVERTER_CONTROL     0x00
#define COLOR_CONVERTER_FUNCTION_R  (0x18 >> 2)
#define COLOR_CONVERTER_ROW_SIZE    (0x20 >> 2)
#define COLOR_CONVERTER_COL_SIZE    (0x28 >> 2)
#define COLOR_CONVERTER_RED_MAG     (0x30 >> 2)
#define COLOR_CONVERTER_GREEN_MAG   (0x38 >> 2)
#define COLOR_CONVERTER_BLUE_MAG    (0x40 >> 2)

#define SOBEL_CONTROL       0x00
#define SOBEL_FUNCTION_R    (0x18 >> 2)
#define SOBEL_ROW_SIZE      (0x20 >> 2)
#define SOBEL_COL_SIZE      (0x28 >> 2)

volatile uint32_t *reg;

int main(){
    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    volatile uint8_t *rd_bmp, *wr_bmp;
    uint32_t blue, green, red;
    int fd4, fd5, fd6, fd7, fd8, fd9;
    volatile uint32_t *axi_dma;
    volatile uint32_t *color_converter_RGB24;
    volatile uint32_t *gaussian_axis_RGB24;
    volatile uint32_t *median_axis_RGB24;
    volatile uint32_t *sobel_axis_RGB24;
    volatile uint8_t *image_buf;
    u_int32_t fd_paddr;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    // Image file open
    if ((fbmpr = fopen(INPUT_IMG_FILE, "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open %s by binary read mode\n", INPUT_IMG_FILE);
        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);
    uint32_t image_all_pixels = bmpihr.biHeight * bmpihr.biWidth;
    uint32_t image_all_bytes = image_all_pixels * IMAGE_CHANNEL;

    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled.
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    image_buf = (volatile uint8_t *)mmap(NULL, 2*image_all_bytes, PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (image_buf == -1){
        fprintf(stderr, "frame_buffer_bmdc mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    fd_paddr = open("/sys/class/u-dma-buf/udmabuf0/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/class/u-dma-buf/udmabuf0/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, (void *)attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);

    // ピクセルを入れるメモリを定義する
    rd_bmp = image_buf;
    wr_bmp = & image_buf[image_all_bytes];

    // 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);
            uint32_t index = (((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x)*IMAGE_CHANNEL;
            rd_bmp[index] = blue;
            rd_bmp[index+1] = green;
            rd_bmp[index+2] = red;
        }
    }
    fclose(fbmpr);

    // uio のオープン
    // axi_dma (UIO 4)
    fd4 = open("/dev/uio4", O_RDWR); // axi_dma interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (axi_dma) open error\n");
        exit(-1);
    }
    axi_dma = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!axi_dma){
        fprintf(stderr, "axi_dma mmap error\n");
        exit(-1);
    }

    // color_converter_RGB24 (UIO 5)
    fd5 = open("/dev/uio5", O_RDWR); // color_converter_RGB24 interface AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (color_converter_RGB24) open error\n");
        exit(-1);
    }
    color_converter_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!color_converter_RGB24){
        fprintf(stderr, "color_converter_RGB24 mmap error\n");
        exit(-1);
    }

    // gaussian_axis_RGB24 (UIO 6)
    fd6 = open("/dev/uio6", O_RDWR); // gaussian_axis_RGB24 interface AXI4 Lite Slave
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (gaussian_axis_RGB24) open error\n");
        exit(-1);
    }
    gaussian_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!gaussian_axis_RGB24){
        fprintf(stderr, "gaussian_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // median_axis_RGB24 (UIO 7)
    fd7 = open("/dev/uio7", O_RDWR); // median_axis_RGB24 interface AXI4 Lite Slave
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (median_axis_RGB24) open error\n");
        exit(-1);
    }
    median_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!median_axis_RGB24){
        fprintf(stderr, "median_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // sobel_axis_RGB24 (UIO 8)
    fd8 = open("/dev/uio8", O_RDWR); // sobel_axis_RGB24 interface AXI4 Lite Slave
    if (fd8 < 1){
        fprintf(stderr, "/dev/uio8 (sobel_axis_RGB24) open error\n");
        exit(-1);
    }
    sobel_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd8, 0);
    if (!sobel_axis_RGB24){
        fprintf(stderr, "sobel_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // gaussian_axis_RGB24
    gaussian_axis_RGB24[GUSSIAN_ROW_SIZE] = bmpihr.biHeight;    // row_size
    gaussian_axis_RGB24[GUSSIAN_COL_SIZE] = bmpihr.biWidth;    // col_size
    gaussian_axis_RGB24[GUSSIAN_FUNCTON_R] = 2;     // function_r, ORG_IMGwAxiDma
    //gaussian_axis_RGB24[GUSSIAN_FUNCTON_R] = 3;   // function_r, GAUSSIANwAxiDma

    // median_axis_RGB24
    median_axis_RGB24[MEDIAN_ROW_SIZE] = bmpihr.biHeight;    // row_size
    median_axis_RGB24[MEDIAN_COL_SIZE] = bmpihr.biWidth;     // col_size
    //median_axis_RGB24[MEDIAN_FUNCTION_R] = 2; // function_r, ORG_IMGwAxiDma
    median_axis_RGB24[MEDIAN_FUNCTION_R] = 3;   // function_r, MEDIANwAxiDma

    // color_converter_RGB24
    color_converter_RGB24[COLOR_CONVERTER_ROW_SIZE] = bmpihr.biHeight;  // row_size
    color_converter_RGB24[COLOR_CONVERTER_COL_SIZE] = bmpihr.biWidth;   // col_size
    color_converter_RGB24[COLOR_CONVERTER_FUNCTION_R] = 2;      // function_r, ORG_IMGwAxiDma
    //color_converter_RGB24[COLOR_CONVERTER_FUNCTION_R] = 3;    // function_r, COLOR_CONVwAxiDma
    color_converter_RGB24[COLOR_CONVERTER_RED_MAG] = 0x2000;    // 32.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>
    color_converter_RGB24[COLOR_CONVERTER_GREEN_MAG] = 0x0100;  // 1.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>
    color_converter_RGB24[COLOR_CONVERTER_BLUE_MAG] = 0x0100;   // 1.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>

    // sobel_axis_RGB24
    sobel_axis_RGB24[SOBEL_ROW_SIZE] = bmpihr.biHeight; // row_size
    sobel_axis_RGB24[SOBEL_COL_SIZE] = bmpihr.biWidth;  // col_size
    //sobel_axis_RGB24[SOBEL_FUNCTION_R] = 2;   // function_r, ORG_IMGwAxiDma
    sobel_axis_RGB24[SOBEL_FUNCTION_R] = 3;     // function_r, MEDIANwAxiDma

    // DMA Start
    // MM2S Channel
    axi_dma[MM2S_CONTROL_REG] = 1;              // MM2S_DMACR, Run/Stop bit(bit 0) = 1
    axi_dma[MM2S_START_ADDR] = phys_addr;       // MM2S_SA
    axi_dma[MM2S_LENGTH_REG] = image_all_bytes; // MM2S_LENGTH(bytes), Start
    // S2MM Channel
    axi_dma[S2MM_CONTROL_REG] = 1;              // S2MM_DMACR, Run/Stop bit(bit 0) = 1
    axi_dma[S2MM_DESTINATION_ADDR] = phys_addr + image_all_bytes; // S2MM_SA
    axi_dma[S2MM_LENGTH_REG] = image_all_bytes; // S2MM_LENGTH(bytes), Start
    // Image IPs start
    gaussian_axis_RGB24[GUSSIAN_CONTROL] = 1;           // start
    median_axis_RGB24[MEDIAN_CONTROL] = 1;              // start
    color_converter_RGB24[COLOR_CONVERTER_CONTROL] = 1; // start
    sobel_axis_RGB24[SOBEL_CONTROL] = 1;                // start

    // Waiting for DMA to finish
    while(!(axi_dma[S2MM_STATUS_REG] & S2MM_IDLE_MASK)) ;   // Waiting Idle bit (S2MM_DMASR, bit 1)

    // Write filtered image file
    if ((fbmpw=fopen(OUTPUT_IMG_FILE, "wb")) == NULL){
        fprintf(stderr, "Can't open %s by binary write mode\n", OUTPUT_IMG_FILE);
        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++){
            uint32_t index = (((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x)*IMAGE_CHANNEL;
            blue = wr_bmp[index];
            green = wr_bmp[index+1];
            red = wr_bmp[index+2];

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);

    munmap((void *)image_buf, 2*image_all_bytes);
    munmap((void *)axi_dma, 0x10000);
    munmap((void *)gaussian_axis_RGB24, 0x10000);
    munmap((void *)median_axis_RGB24, 0x10000);
    munmap((void *)color_converter_RGB24, 0x10000);
    munmap((void *)sobel_axis_RGB24, 0x10000);

    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd8);
    close(fd9);

    return(0);
}

  1. 2023年10月09日 08:34 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる12

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる11”の続き。

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる11”でガウシアン・フィルタ、メディアン・フィルタ、ソーベル・フィルタの回路を C 言語のソースコードで動作させることができた。
しかし、それは、PYNQ で使用している回路ではなく、”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる6”で作成した回路だった。
FPGAの部屋の過去の記事を見返してみると、”kv260_median_platform のメディアン・フィルタを KV260 の Petalinux から動作させる24”が見つかって、その記事での C ソースコードは、Python の PIL ライブラリの image() で読み込める形にできるようなので、これを元に、”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる11”を書き換えようと思う。
ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる5”で Vitis 2022.2 のプラットフォームとアプリケーション・プロジェクトは作成済みなので、そこにソースコードを追加しよう。
  1. 2023年10月08日 04:33 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる11

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる10”の続き。

今まで ZUBoard 1CG の PYNQ Linux で動作させてきた i4filters プロジェクトの回路Petalinux で動作させてみようということで、前回は、zub1cg_i4filters_peta.elf をデバック・モードで起動したが、u-dma-buf のメモリを使用するところで、Segmentation violation でエラーになった。今回は、エラーの原因がわかったので、修正して、もう一度試してみたところ、ソーベル・フィルタ処理後の画像ファイルが生成された。成功だ。

前回のエラーを検証してみたところ、CMA 領域のメモリが足りなかったのが原因だった。
insmod で u-dma-buf で CMA 領域のメモリを確保するが、1920000 バイト歯科確保していなかった。
800 ピクセル x 600 行 x 2 画面分(現画像とフィルタ処理後の画像のメモリ領域)必要なので、800 x 600 x 2 x 4 バイト = 3840000 バイト必要だった。
u-dma-buf を取り除いてから、もう一度 insmod した。
sudo rmmod u-dma-buf.ko
sudo insmod u-dma-buf.ko udmabuf0=3840000

zub1cg_i4filters_84_231006.png

なお、RAMDISK 仕様なので、メモリにファイルをアップロードしても、電源 OFF とともに初期化されるので、電源 ON 時にファイルをアップロードする必要がある。
zub1cg_i4filters_83_231006.png

Vitis 2022.2 の Explorer の zub1cg_i4filters_peta_system を右クリックし右クリックメニューから Debug As -> Debug Configurations... を選択して、Debug Configurations ダイアログを表示した。

ZUBoard 1CG の Petalinux 2022.2 を再度起動したので、IP アドレスが変わってしまった。そのため、Linux TCF Agent を作り直してある。
Debug ボタンをクリックし、デバッグを開始した。
zub1cg_i4filters_85_231006.png

デバッグ画面が表示された。
Resume ボタンをクリックして、ソフトウェアを起動した。
zub1cg_i4filters_86_231006.png

ソフトウェアが終了した。
zub1cg_i4filters_87_231006.png

output_img.bmp ファイルが生成されていた。
zub1cg_i4filters_88_231006.png

output_img.bmp ファイルを SFTP でダウンロードした。
zub1cg_i4filters_89_231006.png

zub1cg_i4filters_90_231006.png

output_img.bmp ファイルを表示した。
メディアン・フィルタ処理でノイズを除去してから、ソーベル・フィルタ処理された画像になっていた。
成功だ。良かった。。。
zub1cg_i4filters_91_231006.jpg
  1. 2023年10月07日 04:11 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる10

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる9”の続き。

今まで ZUBoard 1CG の PYNQ Linux で動作させてきた i4filters プロジェクトの回路Petalinux で動作させてみようということで、前回は、Vits 2022.2 を起動して、”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる6”の XSA ファイルを使用して、zub1cg_i4filters_peta_pf プラットフォーム・プロジェクトと zub1cg_i4filters_peta_system アプリケーション・プロジェクトを作成した。zub1cg_i4filters_peta.c と bmp_header.h を追加して、ビルドし成功した。今回は、前回作成した zub1cg_i4filters_peta.elf をデバック・モードで起動したが、u-dma-buf のメモリを使用するところで、Segmentation violation でエラーになった。

u-dma-buf.ko と使用する画像ファイルの test2.bmp を ZUBoard 1CG の Petalinux に転送した。
zub1cg_i4filters_77_231006.png

u-dma-buf.ko を insmod した。
sudo insmod u-dma-buf.ko udmabuf0=1920000
zub1cg_i4filters_78_231006.png

Vitis 2022.2 の Explorer の zub1cg_i4filters_peta_system を右クリックし右クリックメニューから Debug As -> Debug Configurations... を選択した。

Debug Configurations ダイアログが開いた。
System Project Debug を右クリックし、右クリックメニューから New Configuration を選択した。

System Project Debug の下に SystemDebugger_zub1cg_i4filters_peta_system が生成された。

Linux TCF Agent の New ボタンをクリックして、新しい TCF Agent のエントリを作成する。
Target Connection Details ダイアログが表示された。
Host には ZUBoard 1CG の Petalinux の IP アドレスを入力した。

Test Connection ボタンをクリックした。
connection successful! ダイアログが表示された。

OK ボタンをクリックして、Target Connection Details ダイアログを閉じた。
Debug Configurations ダイアログに戻った。

Remote Working Directory: に /home/petalinux を入力した。
zub1cg_i4filters_79_231006.png

Apply ボタンをクリックし、Debug ボタンをクリックした。

Vitis がデバッグ・モードで起動した。
zub1cg_i4filters_82_231006.png

step over ボタンをクリックし、1行ずつ実行していったが、

rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);


を実行したところで、Segmentation violation でエラーになった。
zub1cg_i4filters_81_231006.png
  1. 2023年10月06日 05:20 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる9

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる8”の続き。

今まで ZUBoard 1CG の PYNQ Linux で動作させてきた i4filters プロジェクトの回路Petalinux で動作させてみようということで、前回は、”u-dma-buf を ZUBoard 1CG 用の Petalinux 2022.2 でビルドする”でビルドした u-dma-buf.ko を SFTP で ZUBoard 1CG の Petalinux 2022.2 にアップロードして、動作を確認した。UIO の状態も確認できた。今回は、Vits 2022.2 を起動して、”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる6”の XSA ファイルを使用して、zub1cg_i4filters_peta_pf プラットフォーム・プロジェクトと zub1cg_i4filters_peta_system アプリケーション・プロジェクトを作成した。zub1cg_i4filters_peta.c と bmp_header.h を追加して、ビルドし成功した。

u-dma-buf を ZUBoard 1CG 用の Petalinux 2022.2 でビルドする”の Vivado から Vitis 2022.2 を起動した。

Vitis IDE Launcher が起動した。
HDL/2023.1/zub1cg/i4filters_peta/vitis_work を新規作成し、Workspace に指定した。

Vitis 2022.2 が起動した。

Create Application Project をクリックし、アプリケーション・プロジェクトを新規作成する。
New Application Project ダイアログの Create a New Application Project が表示された。

Platform
Create a new platform from hardware (XSA) タブをクリックした。
Hardware Specfication の XSA File に /media/masaaki/Ubuntu_Disk/HDL/2023.1/zub1cg/i4filters_peta/i4filters_peta_wrapper.xsa を指定した。
Platform name を i4filters_peta_pf に書き換えた。
zub1cg_i4filters_67_231005.png

Application Project Details
Application project name に zub1cg_i4filters_peta を指定した。
Select target processor for the Appliction project で psu_cortex53 SMP を選択した。
zub1cg_i4filters_68_231005.png

Domain
Application settings の Root FS には、ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta/images/linux/rootfs.ext4 を指定した。
Application settings の Kernel Image には、ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta/images/linux/Image を指定した。
zub1cg_i4filters_69_231005.png

Templates
Linux Empty Application を選択して、Finish ボタンをクリックした。
zub1cg_i4filters_70_231005.png

Explorer に i4filters_peta_pf プラットフォーム・プロジェクトと zub1cg_i4filters_peta_system アプリケーション・プロジェクトが生成された。
zub1cg_i4filters_71_231005.png

Vitis の Explorer から zub1cg_i4filters_peta_system -> zub1cg_i4filters_peta -> src を右クリックし、右クリックメニューから New -> File を選択した。
zub1cg_i4filters_72_231005.png

Create New File ダイアログが表示された。
File name に zub1cg_i4filters_peta.c と入力した。
Finish ボタンをクリックした。
zub1cg_i4filters_73_231005.png

zub1cg_i4filters_peta.c が生成されて、ソースコードを書いた。
zub1cg_i4filters_74_231005.png

同様に bmp_header.h も生成して、ソースコードをコピーした。
zub1cg_i4filters_75_231005.png

ビルドを行って成功した。
zub1cg_i4filters_76_231005.png

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;


zub1cg_i4filters_peta.c を示す。

// zub1cg_i4filters_peta.c
// 2023/10/04 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#include "bmp_header.h"

char INPUT_IMG_FILE[] = "test2.bmp";
char OUTPUT_IMG_FILE[] = "output_img.bmp";

int main(){
    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    volatile uint32_t *rd_bmp, *wr_bmp;
    int blue, green, red;
    int fd4, fd5, fd6, fd7, fd8, fd9;
    volatile uint32_t *axi_dma;
    volatile uint32_t *color_converter_RGB24;
    volatile uint32_t *gaussian_axis_RGB24;
    volatile uint32_t *median_axis_RGB24;
    volatile uint32_t *sobel_axis_RGB24;
    volatile uint32_t *image_buf;
    u_int32_t fd_paddr;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    // Image file open
    if ((fbmpr = fopen(INPUT_IMG_FILE, "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open %s by binary read mode\n", INPUT_IMG_FILE);
        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);
    uint32_t image_all_pixels = bmpihr.biHeight * bmpihr.biWidth;
    uint32_t image_all_bytes = image_all_pixels * sizeof(uint32_t);

    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled.
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    image_buf = (volatile uint32_t *)mmap(NULL, 2*image_all_bytes, PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (image_buf == -1){
        fprintf(stderr, "frame_buffer_bmdc mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    fd_paddr = open("/sys/class/u-dma-buf/udmabuf0/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/class/u-dma-buf/udmabuf0/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, (void *)attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);

    // ピクセルを入れるメモリを定義する
    rd_bmp = image_buf;
    wr_bmp = & image_buf[image_all_pixels];

    // 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);

    // uio のオープン
    // axi_dma (UIO 4)
    fd4 = open("/dev/uio4", O_RDWR); // axi_dma interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (axi_dma) open error\n");
        exit(-1);
    }
    axi_dma = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!axi_dma){
        fprintf(stderr, "axi_dma mmap error\n");
        exit(-1);
    }

    // color_converter_RGB24 (UIO 5)
    fd5 = open("/dev/uio5", O_RDWR); // color_converter_RGB24 interface AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (color_converter_RGB24) open error\n");
        exit(-1);
    }
    color_converter_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!color_converter_RGB24){
        fprintf(stderr, "color_converter_RGB24 mmap error\n");
        exit(-1);
    }

    // gaussian_axis_RGB24 (UIO 6)
    fd6 = open("/dev/uio6", O_RDWR); // gaussian_axis_RGB24 interface AXI4 Lite Slave
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (gaussian_axis_RGB24) open error\n");
        exit(-1);
    }
    gaussian_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!gaussian_axis_RGB24){
        fprintf(stderr, "gaussian_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // median_axis_RGB24 (UIO 7)
    fd7 = open("/dev/uio7", O_RDWR); // median_axis_RGB24 interface AXI4 Lite Slave
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (median_axis_RGB24) open error\n");
        exit(-1);
    }
    median_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!median_axis_RGB24){
        fprintf(stderr, "median_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // sobel_axis_RGB24 (UIO 8)
    fd8 = open("/dev/uio8", O_RDWR); // sobel_axis_RGB24 interface AXI4 Lite Slave
    if (fd8 < 1){
        fprintf(stderr, "/dev/uio8 (sobel_axis_RGB24) open error\n");
        exit(-1);
    }
    sobel_axis_RGB24 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd8, 0);
    if (!sobel_axis_RGB24){
        fprintf(stderr, "sobel_axis_RGB24 mmap error\n");
        exit(-1);
    }

    // gaussian_axis_RGB24
    gaussian_axis_RGB24[8] = bmpihr.biHeight;    // row_size
    gaussian_axis_RGB24[10] = bmpihr.biWidth;    // col_size
    gaussian_axis_RGB24[6] = 2;     // function_r, ORG_IMGwAxiDma
    //gaussian_axis_RGB24[6] = 3;   // function_r, GAUSSIANwAxiDma

    // median_axis_RGB24
    median_axis_RGB24[8] = bmpihr.biHeight;    // row_size
    median_axis_RGB24[10] = bmpihr.biWidth;    // col_size
    //median_axis_RGB24[6] = 2; // function_r, ORG_IMGwAxiDma
    median_axis_RGB24[6] = 3;   // function_r, MEDIANwAxiDma

    // color_converter_RGB24
    color_converter_RGB24[8] = bmpihr.biHeight;    // row_size
    color_converter_RGB24[10] = bmpihr.biWidth;    // col_size
    color_converter_RGB24[6] = 2;       // function_r, ORG_IMGwAxiDma
    //color_converter_RGB24[6] = 3;     // function_r, COLOR_CONVwAxiDma
    color_converter_RGB24[12] = 0x2000;     // 32.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>
    color_converter_RGB24[14] = 0x0100;  // 1.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>
    color_converter_RGB24[16] = 0x0100;   // 1.0, ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>

    // sobel_axis_RGB24
    sobel_axis_RGB24[8] = bmpihr.biHeight;    // row_size
    sobel_axis_RGB24[10] = bmpihr.biWidth;    // col_size
    //sobel_axis_RGB24[6] = 2; // function_r, ORG_IMGwAxiDma
    sobel_axis_RGB24[6] = 3;   // function_r, MEDIANwAxiDma

    // DMA Start
    // MM2S Channel
    axi_dma[0] = 1;         // MM2S_DMACR, Run/Stop bit(bit 0) = 1
    axi_dma[6] = phys_addr; // MM2S_SA
    axi_dma[10] = image_all_bytes; // MM2S_LENGTH(bytes), Start
    // S2MM Channel
    axi_dma[12] = 1;        // S2MM_DMACR, Run/Stop bit(bit 0) = 1
    axi_dma[18] = phys_addr + image_all_bytes; // S2MM_SA
    axi_dma[22] = image_all_bytes; // S2MM_LENGTH(bytes), Start
    // Image IPs start
    gaussian_axis_RGB24[0] = 1; // start
    median_axis_RGB24[0] = 1;   // start
    color_converter_RGB24[0] = 1;   // start
    sobel_axis_RGB24[0] = 1;    // start

    // Waiting for DMA to finish
    while(!(axi_dma[13] & 2)) ;     // Waiting Idle bit (S2MM_DMASR, bit 1)

    // Write filtered image file
    if ((fbmpw=fopen(OUTPUT_IMG_FILE, "wb")) == NULL){
        fprintf(stderr, "Can't open %s by binary write mode\n", OUTPUT_IMG_FILE);
        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++){
            uint32_t pixel = wr_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x];
            blue = pixel & 0xff;
            green = (pixel >> 8) & 0xff;
            red = (pixel >> 16) & 0xff;

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);

    munmap((void *)image_buf, 2*image_all_bytes);
    munmap((void *)axi_dma, 0x10000);
    munmap((void *)gaussian_axis_RGB24, 0x10000);
    munmap((void *)median_axis_RGB24, 0x10000);
    munmap((void *)color_converter_RGB24, 0x10000);
    munmap((void *)sobel_axis_RGB24, 0x10000);

    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd8);
    close(fd9);

    return(0);
}

  1. 2023年10月05日 04:58 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる8

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる7”の続き。

今まで ZUBoard 1CG の PYNQ Linux で動作させてきた i4filters プロジェクトの回路Petalinux で動作させてみようということで、前回は、そのハードウェアに合わせて、Petalinux プロジェクトを作成し、ビルドした。BOOT.BIN を作成し、Micro SD カードにファイルをコピーして、Petalinux 2022.2 を起動した。今回は、”u-dma-buf を ZUBoard 1CG 用の Petalinux 2022.2 でビルドする”でビルドした u-dma-buf.ko を SFTP で ZUBoard 1CG の Petalinux 2022.2 にアップロードして、動作を確認した。UIO の状態も確認できた。

u-dma-buf を ZUBoard 1CG 用の Petalinux 2022.2 でビルドする”でビルドした u-dma-buf.ko を SFTP で ZUBoard 1CG の Petalinux 2022.2 にアップロードした。
zub1cg_i4filters_62_231003.png

u-dma-buf.ko を insmod した。udmabuf0 インスタンスを作成した。
sudo insmod u-dma-buf.ko udmabuf0=0x10000
成功した。物理アドレス 0x0000000027050000 にマップされた。サイズは 65536 だった。
zub1cg_i4filters_63_231003.png

/sys/class/u-dma-buf/udmabuf0 を確認した。
zub1cg_i4filters_65_231003.png

/sys/class/uio の UIO を確認した。
zub1cg_i4filters_64_231003.png
zub1cg_i4filters_66_231003.png

UIO のマップは”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる3”と同様だった。

uio4: dma                   0xa0000000
uio5: color_converter_RGB24 0xa0040000
uio6: gaussian_axis_RGB24   0xa0030000
uio7: median_axis_RGB24     0xa0020000
uio8: sobel_axis_RGB24      0xa0010000

  1. 2023年10月04日 04:12 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

u-dma-buf を ZUBoard 1CG 用の Petalinux 2022.2 でビルドする

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる6”の AXI DMA IP を動作させるために u-dma-buf が必要となるので、”ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる7”の Petalinux 2022.2 で u-dma-buf をビルドした。

i4filters_peta ディレクトリには、ZUBoard 1CG 用の Petalinux 2022.2 がビルドされているので、これで u-dma-buf をビルドする。

Petalinux 2022.2 のプロジェクトの /media/masaaki/Ubuntu_Disk/ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta ディレクトリに移動してして、u-dma-buf をカーネル・モジュールとして生成する。なお、Petalinux 2022.2 の settings.sh は実行済みだ。
petalinux-create -t modules --name u-dma-buf --enable
zub1cg_i4filters_56_231002.png

ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta/project-spec/meta-user/recipes-modulesディレクトリの下に u-dma-buf ディレクトリが作成され、その下の files ディレクトリの下に u-dma-buf.c が生成された。
zub1cg_i4filters_61_231002.png

rootfs をビルドした。
petalinux-build -c rootfs
zub1cg_i4filters_57_231002.png

ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta/project-spec/meta-user/recipes-modules/u-dma-buf/files/u-dma-buf.c に ikwzm/udambuf/u-dma-buf.c の内容をコピペした。
zub1cg_i4filters_58_231002.png

u-dma-buf をビルドした。
petalinux-build -c u-dma-buf
成功した。
zub1cg_i4filters_59_231002.png

ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters_peta/build/tmp/sysroots-components/zynqmp_generic/u-dma-buf/lib/modules/5.15.36-xilinx-v2022.2/extra ディレクトリに u-dma-buf.ko が作成された。
zub1cg_i4filters_60_231002.png
  1. 2023年10月03日 03:26 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる7

ZUBoard 1CG の i4filters プロジェクトの回路を Petalinux で動作させる6”の続き。

今まで ZUBoard 1CG の PYNQ Linux で動作させてきた i4filters プロジェクトの回路Petalinux で動作させてみようということで、前回は、アプリケーション・プロジェクトでソフトウェアを書きながら OpenCV のインストールを試していたのだが、やはりインストールできなかった。仕方がないので、bmp_header.h を使用して、BMP ファイルをロードしてやることにしたが、そのためにはハードウェアを変更する必要がある。ということで、ハードウェアを変更することにした。今回は、そのハードウェアに合わせて、Petalinux プロジェクトを作成し、ビルドした。BOOT.BIN を作成し、Micro SD カードにファイルをコピーして、Petalinux 2022.2 を起動した。

Petalinux 2022.2 の zub1cg_i4filters_peta プロジェクトを作成する。
cd /media/masaaki/Ubuntu_Disk/ZUBoard1CG/Avnet_2022_2/petalinux/projects
petalinux-create -t project -n zub1cg_i4filters --template zynqMP


zub1cg_i4filters ディレクトリに行って、ハードウェアの XSA ファイルの設定を適用した。
cd zub1cg_i4filters
petalinux-config --get-hw-description=/media/masaaki/Ubuntu_Disk/HDL/2023.1/zub1cg/i4filters


misc/config System Configuration が起動した。
デフォルトのままにした。
セーブし終了した。

ZUBoard1CG/Avnet_2022_2/petalinux/projects/zub1cg_i4filters/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi を書き換えて、UIO のエントリを追加した。

/include/ "system-conf.dtsi"
/ {
    chosen {
        bootargs = "uio_pdrv_genirq.of_id=generic-uio";
    };
};

    &axi_dma_0{
        compatible = "generic-uio";
    };
    
    &color_converter_RGB24_0{
        compatible = "generic-uio";
    };
    
    &gaussian_axis_RGB24_0{
        compatible = "generic-uio";
    };

    &median_axis_RGB24_0{
        compatible = "generic-uio";
    };
    
    &sobel_axis_RGB24_0{
        compatible = "generic-uio";
    };
    
    &sdhci1 {
        status = "okay";
        no-1-8-v;
        disable-wp;
    };


RootFS を編集した。
petalinux-config -c rootfs

Filesystem Package -> x11 -> pcmanfm の pcmanfm に * を付けた。
Filesystem Package -> console -> network -> openssh の openssh-sftp-server に * を付けた。
Filesystem Package -> misc -> l3afpad の l3afpad に * を付けた。
packagegroup-petalinux-x11 を追加した。
Image Features -> package-management に * を追加した。
セーブして終了した。

ビルドを行って成功した。
petalinux-build

BOOT.BIN を作成した。
cd images/linux
petalinux-package --boot --fsbl zynqmp_fsbl.elf --u-boot u-boot.elf --pmufw pmufw.elf --fpga system.bit --force


ディレクトリの様子を示す。
zub1cg_i4filters_54_231002.png

ZUBoard1CG/at_2022_2/zub1cg_gpio/images/linux ディレクトリの BOOT.BIN, boot.scr, image.ub を FAT32 でフォーマットされた MicroSD カードに書き込んだ。

Micro SD カードを ZUBoard 1CG に挿入し電源ONした。
Petalinux 2022.2 が起動した。
zub1cg_i4filters_55_231002.png

起動ログを貼っておく。

NOTICE:  BL31: v2.6(release):xlnx_rebase_v2.6_2022.1_update3-18-g0897efd45
NOTICE:  BL31: Built : 03:55:03, Sep  9 2022


U-Boot 2022.01 (Sep 20 2022 - 06:35:33 +0000)

CPU:   ZynqMP
Silicon: v3
Board: Xilinx ZynqMP
DRAM:  1023 MiB
PMUFW:  v1.1
PMUFW no permission to change config object
EL Level:   EL2
Chip ID:    zu1eg
NAND:  0 MiB
MMC:   mmc@ff170000: 0
Loading Environment from FAT... *** Error - No Valid Environment Area found
*** Warning - bad env area, using default environment

In:    serial
Out:   serial
Err:   serial
Bootmode: SD_MODE1
Reset reason:   EXTERNAL 
Net:   FEC: can't find phy-handle

ZYNQ GEM: ff0d0000, mdio bus ff0d0000, phyaddr 7, interface rgmii-id

Warning: ethernet@ff0d0000 (eth0) using random MAC address - aa:f8:2c:57:0c:03
eth0: ethernet@ff0d0000
scanning bus for devices...
starting USB...
No working controllers found
Hit any key to stop autoboot:  0 
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found U-Boot script /boot.scr
2777 bytes read in 13 ms (208 KiB/s)
## Executing script at 20000000
Trying to load boot images from mmc0
86728516 bytes read in 6164 ms (13.4 MiB/s)
## Loading kernel from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Trying 'kernel-1' kernel subimage
     Description:  Linux kernel
     Created:      2022-10-03   7:50:07 UTC
     Type:         Kernel Image
     Compression:  gzip compressed
     Data Start:   0x100000fc
     Data Size:    9268952 Bytes = 8.8 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0x00200000
     Entry Point:  0x00200000
     Hash algo:    sha256
     Hash value:   0a45931a185d9da44b6d5e41c6b7e68412ab963794be99aef85124f428fb76a2
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Trying 'ramdisk-1' ramdisk subimage
     Description:  petalinux-image-minimal
     Created:      2022-10-03   7:50:07 UTC
     Type:         RAMDisk Image
     Compression:  uncompressed
     Data Start:   0x108e0aec
     Data Size:    77418206 Bytes = 73.8 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: unavailable
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   ff1d7c1578de26fece5d4358eaac333164748fce77c8d75ec131848af5ba36bc
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Trying 'fdt-system-top.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Created:      2022-10-03   7:50:07 UTC
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x108d70e4
     Data Size:    39223 Bytes = 38.3 KiB
     Architecture: AArch64
     Hash algo:    sha256
     Hash value:   f7c8b5875bb51734ec5d4e2c015badfb626df1ed3bc97442d824c6f34b44ae08
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x108d70e4
   Uncompressing Kernel Image
   Loading Ramdisk to 37226000, end 3bbfaede ... OK
   Loading Device Tree to 0000000037219000, end 0000000037225936 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[    0.000000] Linux version 5.15.36-xilinx-v2022.2 (oe-user@oe-host) (aarch64-xilinx-linux-gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37.20210721) #1 SMP Mon Oct 3 07:50:07 UTC 2022
[    0.000000] Machine model: xlnx,zynqmp
[    0.000000] efi: UEFI not found.
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000000000000-0x000000003fefffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000000000-0x000000003fefffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x000000003fefffff]
[    0.000000] On node 0, zone DMA32: 256 pages in unavailable ranges
[    0.000000] cma: Reserved 256 MiB at 0x0000000027000000
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] percpu: Embedded 18 pages/cpu s34776 r8192 d30760 u73728
[    0.000000] Detected VIPT I-cache on CPU0
[    0.000000] CPU features: detected: ARM erratum 845719
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 257796
[    0.000000] Kernel command line: uio_pdrv_genirq.of_id=generic-uio
[    0.000000] Dentry cache hash table entries: 131072 (order: 8, 1048576 bytes, linear)
[    0.000000] Inode-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 667776K/1047552K available (13888K kernel code, 990K rwdata, 3916K rodata, 2176K init, 573K bss, 117632K reserved, 262144K cma-reserved)
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=16 to nr_cpu_ids=2.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] GIC: Adjusting CPU interface base to 0x00000000f902f000
[    0.000000] Root IRQ handler: gic_handle_irq
[    0.000000] GIC: Using split EOI/Deactivate mode
[    0.000000] random: get_random_bytes called from start_kernel+0x474/0x6d8 with crng_init=0
[    0.000000] arch_timer: cp15 timer(s) running at 100.00MHz (phys).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x171024e7e0, max_idle_ns: 440795205315 ns
[    0.000000] sched_clock: 56 bits at 100MHz, resolution 10ns, wraps every 4398046511100ns
[    0.000364] Console: colour dummy device 80x25
[    0.000603] printk: console [tty0] enabled
[    0.000639] Calibrating delay loop (skipped), value calculated using timer frequency.. 200.00 BogoMIPS (lpj=400000)
[    0.000660] pid_max: default: 32768 minimum: 301
[    0.000929] Mount-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.000952] Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.002344] rcu: Hierarchical SRCU implementation.
[    0.002594] EFI services will not be available.
[    0.002711] smp: Bringing up secondary CPUs ...
[    0.003160] Detected VIPT I-cache on CPU1
[    0.003216] CPU1: Booted secondary processor 0x0000000001 [0x410fd034]
[    0.003304] smp: Brought up 1 node, 2 CPUs
[    0.003329] SMP: Total of 2 processors activated.
[    0.003339] CPU features: detected: 32-bit EL0 Support
[    0.003349] CPU features: detected: CRC32 instructions
[    0.003400] CPU: All CPU(s) started at EL2
[    0.003420] alternatives: patching kernel code
[    0.004504] devtmpfs: initialized
[    0.008888] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.008917] futex hash table entries: 512 (order: 3, 32768 bytes, linear)
[    0.018620] pinctrl core: initialized pinctrl subsystem
[    0.019275] DMI not present or invalid.
[    0.019582] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    0.020843] DMA: preallocated 128 KiB GFP_KERNEL pool for atomic allocations
[    0.020934] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.020995] audit: initializing netlink subsys (disabled)
[    0.021101] audit: type=2000 audit(0.020:1): state=initialized audit_enabled=0 res=1
[    0.021512] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers.
[    0.021563] ASID allocator initialised with 65536 entries
[    0.021692] Serial: AMBA PL011 UART driver
[    0.039601] HugeTLB registered 1.00 GiB page size, pre-allocated 0 pages
[    0.039635] HugeTLB registered 32.0 MiB page size, pre-allocated 0 pages
[    0.039646] HugeTLB registered 2.00 MiB page size, pre-allocated 0 pages
[    0.039657] HugeTLB registered 64.0 KiB page size, pre-allocated 0 pages
[    1.109434] cryptd: max_cpu_qlen set to 1000
[    1.134814] DRBG: Continuing without Jitter RNG
[    1.237095] raid6: neonx8   gen()  2140 MB/s
[    1.305150] raid6: neonx8   xor()  1586 MB/s
[    1.373216] raid6: neonx4   gen()  2182 MB/s
[    1.441272] raid6: neonx4   xor()  1555 MB/s
[    1.509343] raid6: neonx2   gen()  2066 MB/s
[    1.577396] raid6: neonx2   xor()  1430 MB/s
[    1.645472] raid6: neonx1   gen()  1762 MB/s
[    1.713522] raid6: neonx1   xor()  1212 MB/s
[    1.781591] raid6: int64x8  gen()  1366 MB/s
[    1.849647] raid6: int64x8  xor()   773 MB/s
[    1.917708] raid6: int64x4  gen()  1599 MB/s
[    1.985778] raid6: int64x4  xor()   851 MB/s
[    2.053853] raid6: int64x2  gen()  1396 MB/s
[    2.121911] raid6: int64x2  xor()   749 MB/s
[    2.189980] raid6: int64x1  gen()  1033 MB/s
[    2.258034] raid6: int64x1  xor()   517 MB/s
[    2.258045] raid6: using algorithm neonx4 gen() 2182 MB/s
[    2.258054] raid6: .... xor() 1555 MB/s, rmw enabled
[    2.258063] raid6: using neon recovery algorithm
[    2.258675] iommu: Default domain type: Translated 
[    2.258688] iommu: DMA domain TLB invalidation policy: strict mode 
[    2.258955] SCSI subsystem initialized
[    2.259142] usbcore: registered new interface driver usbfs
[    2.259182] usbcore: registered new interface driver hub
[    2.259211] usbcore: registered new device driver usb
[    2.259291] mc: Linux media interface: v0.10
[    2.259318] videodev: Linux video capture interface: v2.00
[    2.259365] pps_core: LinuxPPS API ver. 1 registered
[    2.259375] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    2.259397] PTP clock support registered
[    2.259431] EDAC MC: Ver: 3.0.0
[    2.259778] zynqmp-ipi-mbox mailbox@ff990400: Registered ZynqMP IPI mbox with TX/RX channels.
[    2.260028] FPGA manager framework
[    2.260171] Advanced Linux Sound Architecture Driver Initialized.
[    2.260578] Bluetooth: Core ver 2.22
[    2.260609] NET: Registered PF_BLUETOOTH protocol family
[    2.260619] Bluetooth: HCI device and connection manager initialized
[    2.260635] Bluetooth: HCI socket layer initialized
[    2.260646] Bluetooth: L2CAP socket layer initialized
[    2.260663] Bluetooth: SCO socket layer initialized
[    2.261096] clocksource: Switched to clocksource arch_sys_counter
[    2.261265] VFS: Disk quotas dquot_6.6.0
[    2.261319] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[    2.265902] NET: Registered PF_INET protocol family
[    2.266022] IP idents hash table entries: 16384 (order: 5, 131072 bytes, linear)
[    2.266662] tcp_listen_portaddr_hash hash table entries: 512 (order: 1, 8192 bytes, linear)
[    2.266693] TCP established hash table entries: 8192 (order: 4, 65536 bytes, linear)
[    2.266760] TCP bind hash table entries: 8192 (order: 5, 131072 bytes, linear)
[    2.266930] TCP: Hash tables configured (established 8192 bind 8192)
[    2.267045] UDP hash table entries: 512 (order: 2, 16384 bytes, linear)
[    2.267078] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes, linear)
[    2.267180] NET: Registered PF_UNIX/PF_LOCAL protocol family
[    2.267533] RPC: Registered named UNIX socket transport module.
[    2.267545] RPC: Registered udp transport module.
[    2.267554] RPC: Registered tcp transport module.
[    2.267562] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    2.267578] PCI: CLS 0 bytes, default 64
[    2.267746] Trying to unpack rootfs image as initramfs...
[    2.274014] armv8-pmu pmu: hw perfevents: no interrupt-affinity property, guessing.
[    2.274653] hw perfevents: enabled with armv8_pmuv3 PMU driver, 7 counters available
[    6.035995] Freeing initrd memory: 75600K
[    6.099817] Initialise system trusted keyrings
[    6.099982] workingset: timestamp_bits=46 max_order=18 bucket_order=0
[    6.100835] NFS: Registering the id_resolver key type
[    6.100857] Key type id_resolver registered
[    6.100866] Key type id_legacy registered
[    6.100892] nfs4filelayout_init: NFSv4 File Layout Driver Registering...
[    6.100904] nfs4flexfilelayout_init: NFSv4 Flexfile Layout Driver Registering...
[    6.100934] jffs2: version 2.2. (NAND) (SUMMARY)  © 2001-2006 Red Hat, Inc.
[    6.137025] NET: Registered PF_ALG protocol family
[    6.137043] xor: measuring software checksum speed
[    6.141235]    8regs           :  2363 MB/sec
[    6.144760]    32regs          :  2799 MB/sec
[    6.149038]    arm64_neon      :  2307 MB/sec
[    6.149048] xor: using function: 32regs (2799 MB/sec)
[    6.149060] Key type asymmetric registered
[    6.149070] Asymmetric key parser 'x509' registered
[    6.149128] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 244)
[    6.149143] io scheduler mq-deadline registered
[    6.149154] io scheduler kyber registered
[    6.178215] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    6.179540] Serial: AMBA driver
[    6.180377] cacheinfo: Unable to detect cache hierarchy for CPU 0
[    6.185394] brd: module loaded
[    6.189002] loop: module loaded
[    6.189985] mtdoops: mtd device (mtddev=name/number) must be supplied
[    6.193139] tun: Universal TUN/TAP device driver, 1.6
[    6.193235] CAN device driver interface
[    6.193884] usbcore: registered new interface driver asix
[    6.193950] usbcore: registered new interface driver ax88179_178a
[    6.193980] usbcore: registered new interface driver cdc_ether
[    6.194012] usbcore: registered new interface driver net1080
[    6.194046] usbcore: registered new interface driver cdc_subset
[    6.194076] usbcore: registered new interface driver zaurus
[    6.194119] usbcore: registered new interface driver cdc_ncm
[    6.194883] usbcore: registered new interface driver uas
[    6.194926] usbcore: registered new interface driver usb-storage
[    6.195724] rtc_zynqmp ffa60000.rtc: registered as rtc0
[    6.195751] rtc_zynqmp ffa60000.rtc: setting system clock to 1970-01-01T00:00:11 UTC (11)
[    6.195822] i2c_dev: i2c /dev entries driver
[    6.197547] usbcore: registered new interface driver uvcvideo
[    6.198003] Bluetooth: HCI UART driver ver 2.3
[    6.198016] Bluetooth: HCI UART protocol H4 registered
[    6.198026] Bluetooth: HCI UART protocol BCSP registered
[    6.198052] Bluetooth: HCI UART protocol LL registered
[    6.198062] Bluetooth: HCI UART protocol ATH3K registered
[    6.198084] Bluetooth: HCI UART protocol Three-wire (H5) registered
[    6.198130] Bluetooth: HCI UART protocol Intel registered
[    6.198154] Bluetooth: HCI UART protocol QCA registered
[    6.198188] usbcore: registered new interface driver bcm203x
[    6.198221] usbcore: registered new interface driver bpa10x
[    6.198256] usbcore: registered new interface driver bfusb
[    6.198291] usbcore: registered new interface driver btusb
[    6.198340] usbcore: registered new interface driver ath3k
[    6.198434] EDAC MC: ECC not enabled
[    6.198589] EDAC DEVICE0: Giving out device to module edac controller cache_err: DEV edac (POLLED)
[    6.198765] EDAC DEVICE1: Giving out device to module zynqmp-ocm-edac controller zynqmp_ocm: DEV ff960000.memory-controller (INTERRUPT)
[    6.199251] sdhci: Secure Digital Host Controller Interface driver
[    6.199262] sdhci: Copyright(c) Pierre Ossman
[    6.199270] sdhci-pltfm: SDHCI platform and OF driver helper
[    6.199567] ledtrig-cpu: registered to indicate activity on CPUs
[    6.199700] SMCCC: SOC_ID: ARCH_SOC_ID not implemented, skipping ....
[    6.199786] zynqmp_firmware_probe Platform Management API v1.1
[    6.199801] zynqmp_firmware_probe Trustzone version v1.0
[    6.228028] securefw securefw: securefw probed
[    6.228143] zynqmp_aes firmware:zynqmp-firmware:zynqmp-aes: The zynqmp-aes driver shall be deprecated in 2022.2 and removed in 2023.1
[    6.228441] alg: No test for xilinx-zynqmp-aes (zynqmp-aes)
[    6.228473] zynqmp_aes firmware:zynqmp-firmware:zynqmp-aes: AES Successfully Registered
[    6.228604] zynqmp-keccak-384 firmware:zynqmp-firmware:sha384: The zynqmp-sha-deprecated driver shall be deprecated in 2022.2 and removed in 2023.1 release
[    6.228704] alg: No test for xilinx-keccak-384 (zynqmp-keccak-384)
[    6.228904] alg: No test for xilinx-zynqmp-rsa (zynqmp-rsa)
[    6.229124] usbcore: registered new interface driver usbhid
[    6.229136] usbhid: USB HID core driver
[    6.232367] ARM CCI_400_r1 PMU driver probed
[    6.233025] fpga_manager fpga0: Xilinx ZynqMP FPGA Manager registered
[    6.233486] usbcore: registered new interface driver snd-usb-audio
[    6.234326] pktgen: Packet Generator for packet performance testing. Version: 2.75
[    6.234910] Initializing XFRM netlink socket
[    6.235006] NET: Registered PF_INET6 protocol family
[    6.235635] Segment Routing with IPv6
[    6.235660] In-situ OAM (IOAM) with IPv6
[    6.235749] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    6.236153] NET: Registered PF_PACKET protocol family
[    6.236176] NET: Registered PF_KEY protocol family
[    6.236202] can: controller area network core
[    6.236246] NET: Registered PF_CAN protocol family
[    6.236257] can: raw protocol
[    6.236268] can: broadcast manager protocol
[    6.236280] can: netlink gateway - max_hops=1
[    6.236384] Bluetooth: RFCOMM TTY layer initialized
[    6.236401] Bluetooth: RFCOMM socket layer initialized
[    6.236431] Bluetooth: RFCOMM ver 1.11
[    6.236446] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[    6.236456] Bluetooth: BNEP filters: protocol multicast
[    6.236469] Bluetooth: BNEP socket layer initialized
[    6.236478] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
[    6.236491] Bluetooth: HIDP socket layer initialized
[    6.236535] 8021q: 802.1Q VLAN Support v1.8
[    6.236659] 9pnet: Installing 9P2000 support
[    6.236696] Key type dns_resolver registered
[    6.236826] registered taskstats version 1
[    6.236837] Loading compiled-in X.509 certificates
[    6.238323] Btrfs loaded, crc32c=crc32c-generic, zoned=no, fsverity=no
[    6.249544] ff000000.serial: ttyPS0 at MMIO 0xff000000 (irq = 59, base_baud = 6249999) is a xuartps
[    7.585577] printk: console [ttyPS0] enabled
[    7.590315] of-fpga-region fpga-full: FPGA Region probed
[    7.597630] xilinx-zynqmp-dma fd500000.dma-controller: ZynqMP DMA driver Probe success
[    7.605735] xilinx-zynqmp-dma fd510000.dma-controller: ZynqMP DMA driver Probe success
[    7.613837] xilinx-zynqmp-dma fd520000.dma-controller: ZynqMP DMA driver Probe success
[    7.621943] xilinx-zynqmp-dma fd530000.dma-controller: ZynqMP DMA driver Probe success
[    7.630051] xilinx-zynqmp-dma fd540000.dma-controller: ZynqMP DMA driver Probe success
[    7.638164] xilinx-zynqmp-dma fd550000.dma-controller: ZynqMP DMA driver Probe success
[    7.646264] xilinx-zynqmp-dma fd560000.dma-controller: ZynqMP DMA driver Probe success
[    7.654365] xilinx-zynqmp-dma fd570000.dma-controller: ZynqMP DMA driver Probe success
[    7.662539] xilinx-zynqmp-dma ffa80000.dma-controller: ZynqMP DMA driver Probe success
[    7.670638] xilinx-zynqmp-dma ffa90000.dma-controller: ZynqMP DMA driver Probe success
[    7.678735] xilinx-zynqmp-dma ffaa0000.dma-controller: ZynqMP DMA driver Probe success
[    7.686835] xilinx-zynqmp-dma ffab0000.dma-controller: ZynqMP DMA driver Probe success
[    7.694948] xilinx-zynqmp-dma ffac0000.dma-controller: ZynqMP DMA driver Probe success
[    7.703048] xilinx-zynqmp-dma ffad0000.dma-controller: ZynqMP DMA driver Probe success
[    7.711150] xilinx-zynqmp-dma ffae0000.dma-controller: ZynqMP DMA driver Probe success
[    7.719257] xilinx-zynqmp-dma ffaf0000.dma-controller: ZynqMP DMA driver Probe success
[    7.727977] zynqmp-qspi ff0f0000.spi: rx bus width not found
[    7.733641] zynqmp-qspi ff0f0000.spi: tx bus width not found
[    7.739408] spi_master spi0: cannot find modalias for /axi/spi@ff0f0000/flash@0
[    7.746729] spi_master spi0: Failed to create SPI device for /axi/spi@ff0f0000/flash@0
[    7.755005] macb ff0d0000.ethernet: Not enabling partial store and forward
[    7.767977] macb ff0d0000.ethernet eth0: Cadence GEM rev 0x50070106 at 0xff0d0000 irq 37 (aa:f8:2c:57:0c:03)
[    7.778354] zynqmp_pll_disable() clock disable failed for apll_int, ret = -13
[    7.785647] xilinx-axipmon ffa00000.perf-monitor: Probed Xilinx APM
[    7.792231] xilinx-axipmon fd0b0000.perf-monitor: Probed Xilinx APM
[    7.798739] xilinx-axipmon fd490000.perf-monitor: Probed Xilinx APM
[    7.805279] xilinx-axipmon ffa10000.perf-monitor: Probed Xilinx APM
[    7.814272] cdns-i2c ff030000.i2c: 400 kHz mmio ff030000 irq 39
[    7.820647] cdns-wdt fd4d0000.watchdog: Xilinx Watchdog Timer with timeout 60s
[    7.828126] cdns-wdt ff150000.watchdog: Xilinx Watchdog Timer with timeout 10s
[    7.839660] of_cfs_init
[    7.842186] of_cfs_init: OK
[    7.846261] ALSA device list:
[    7.849226]   No soundcards found.
[    7.869485] mmc0: SDHCI controller on ff170000.mmc [ff170000.mmc] using ADMA 64-bit
[    7.878260] Freeing unused kernel memory: 2176K
[    7.883100] Run /init as init process
[    7.900455] systemd[1]: System time before build time, advancing clock.
[    7.911456] systemd[1]: systemd 249.7+ running in system mode (+PAM -AUDIT -SELINUX -APPARMOR +IMA -SMACK +SECCOMP -GCRYPT -GNUTLS -OPENSSL +ACL +BLKID -CURL -ELFUTILS -FIDO2 -IDN2 -IDN -IPTC +KMOD -LIBCRYPTSETUP +LIBFDISK -PCRE2 -PWQUALITY -P11KIT -QRENCODE -BZIP2 -LZ4 -XZ -ZLIB +ZSTD +XKBCOMMON +UTMP +SYSVINIT default-hierarchy=hybrid)
[    7.941801] systemd[1]: Detected architecture arm64.
[    7.952839] mmc0: Problem switching card into high-speed mode!
[    7.961308] mmc0: new SDHC card at address 0001

Welcome to PetaLinux 2022.2_update1_04022314 (honister)[    7.966557] mmcblk0: mmc0:0001 SPCC  29.0 GiB 
!

[    7.976923] systemd[1]: Hostname set to <zub1cgi4filterspeta>.
[    7.982986] random: systemd: uninitialized urandom read (16 bytes read)
[    7.990275] systemd[1]: Initializing machine ID from random generator.
[    7.996879]  mmcblk0: p1 p2
[    8.056096] systemd-sysv-generator[217]: SysV service '/etc/init.d/watchdog-init' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    8.083557] systemd-sysv-generator[217]: SysV service '/etc/init.d/nfsserver' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    8.107617] systemd-sysv-generator[217]: SysV service '/etc/init.d/nfscommon' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    8.131944] systemd-sysv-generator[217]: SysV service '/etc/init.d/inetd.busybox' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    8.157054] systemd-sysv-generator[217]: SysV service '/etc/init.d/dropbear' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    8.381723] systemd[1]: Queued start job for default target Graphical Interface.
[    8.390287] random: systemd: uninitialized urandom read (16 bytes read)
[    8.419745] systemd[1]: Created slice Slice /system/getty.
[  OK  ] Created slice Slice /system/getty.
[    8.441261] random: systemd: uninitialized urandom read (16 bytes read)
[    8.449378] systemd[1]: Created slice Slice /system/modprobe.
[  OK  ] Created slice Slice /system/modprobe.
[    8.470455] systemd[1]: Created slice Slice /system/serial-getty.
[  OK  ] Created slice Slice /system/serial-getty.
[    8.494282] systemd[1]: Created slice User and Session Slice.
[  OK  ] Created slice User and Session Slice.
[    8.517401] systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
[  OK  ] Started Dispatch Password …ts to Console Directory Watch.
[    8.541301] systemd[1]: Started Forward Password Requests to Wall Directory Watch.
[  OK  ] Started Forward Password R…uests to Wall Directory Watch.
[    8.565358] systemd[1]: Reached target Path Units.
[  OK  ] Reached target Path Units.
[    8.581196] systemd[1]: Reached target Remote File Systems.
[  OK  ] Reached target Remote File Systems.
[    8.601191] systemd[1]: Reached target Slice Units.
[  OK  ] Reached target Slice Units.
[    8.617203] systemd[1]: Reached target Swaps.
[  OK  ] Reached target Swaps.
[    8.633749] systemd[1]: Listening on RPCbind Server Activation Socket.
[  OK  ] Listening on RPCbind Server Activation Socket.
[    8.657185] systemd[1]: Reached target RPC Port Mapper.
[  OK  ] Reached target RPC Port Mapper.
[    8.677463] systemd[1]: Listening on Syslog Socket.
[  OK  ] Listening on Syslog Socket.
[    8.693367] systemd[1]: Listening on initctl Compatibility Named Pipe.
[  OK  ] Listening on initctl Compatibility Named Pipe.
[    8.717709] systemd[1]: Listening on Journal Audit Socket.
[  OK  ] Listening on Journal Audit Socket.
[    8.737407] systemd[1]: Listening on Journal Socket (/dev/log).
[  OK  ] Listening on Journal Socket (/dev/log).
[    8.761491] systemd[1]: Listening on Journal Socket.
[  OK  ] Listening on Journal Socket.
[    8.777641] systemd[1]: Listening on Network Service Netlink Socket.
[  OK  ] Listening on Network Service Netlink Socket.
[    8.801485] systemd[1]: Listening on udev Control Socket.
[  OK  ] Listening on udev Control Socket.
[    8.821392] systemd[1]: Listening on udev Kernel Socket.
[  OK  ] Listening on udev Kernel Socket.
[    8.841420] systemd[1]: Listening on User Database Manager Socket.
[  OK  ] Listening on User Database Manager Socket.
[    8.868215] systemd[1]: Mounting Huge Pages File System...
         Mounting Huge Pages File System...
[    8.888500] systemd[1]: Mounting POSIX Message Queue File System...
         Mounting POSIX Message Queue File System...
[    8.912583] systemd[1]: Mounting Kernel Debug File System...
         Mounting Kernel Debug File System...
[    8.929603] systemd[1]: Condition check resulted in Kernel Trace File System being skipped.
[    8.941495] systemd[1]: Mounting Temporary Directory /tmp...
         Mounting Temporary Directory /tmp...
[    8.957564] systemd[1]: Condition check resulted in Create List of Static Device Nodes being skipped.
[    8.970602] systemd[1]: Starting Load Kernel Module configfs...
         Starting Load Kernel Module configfs...
[    8.993269] systemd[1]: Starting Load Kernel Module drm...
         Starting Load Kernel Module drm...
[    9.012835] systemd[1]: Starting Load Kernel Module fuse...
         Starting Load Kernel Module fuse...
[    9.032899] systemd[1]: Starting RPC Bind...
         Starting RPC Bind...
[    9.049367] systemd[1]: Condition check resulted in File System Check on Root Device being skipped.
[    9.059226] systemd[1]: Condition check resulted in Load Kernel Modules being skipped.
[    9.070200] systemd[1]: Mounting NFSD configuration filesystem...
         Mounting NFSD configuration filesystem...
[    9.088793] systemd[1]: Starting Remount Root and Kernel File Systems...
         Starting Remount Root and Kernel File Systems...
[    9.117007] systemd[1]: Starting Apply Kernel Variables...
         Starting Apply Kernel Variables...
[    9.136921] systemd[1]: Starting Coldplug All udev Devices...
         Starting Coldplug All udev Devices...
[    9.158380] systemd[1]: Started RPC Bind.
[  OK  ] Started RPC Bind.
[    9.173929] systemd[1]: Mounted Huge Pages File System.
[  OK  ] Mounted Huge Pages File System.
[    9.197913] systemd[1]: Mounted POSIX Message Queue File System.
[  OK  ] Mounted POSIX Message Queue File System.
[    9.225913] systemd[1]: Mounted Kernel Debug File System.
[  OK  ] Mounted Kernel Debug File System.
[    9.254010] systemd[1]: Mounted Temporary Directory /tmp.
[  OK  ] Mounted Temporary Directory /tmp.
[    9.278498] systemd[1]: modprobe@configfs.service: Deactivated successfully.
[    9.287039] systemd[1]: Finished Load Kernel Module configfs.
[  OK  ] Finished Load Kernel Module configfs.
[    9.309983] systemd[1]: modprobe@drm.service: Deactivated successfully.
[    9.317828] systemd[1]: Finished Load Kernel Module drm.
[  OK  ] Finished Load Kernel Module drm.
[    9.337899] systemd[1]: modprobe@fuse.service: Deactivated successfully.
[    9.345816] systemd[1]: Finished Load Kernel Module fuse.
[  OK  ] Finished Load Kernel Module fuse.
[    9.369526] systemd[1]: proc-fs-nfsd.mount: Mount process exited, code=exited, status=32/n/a
[    9.377991] systemd[1]: proc-fs-nfsd.mount: Failed with result 'exit-code'.
[    9.386381] systemd[1]: Failed to mount NFSD configuration filesystem.
[FAILED] Failed to mount NFSD configuration filesystem.
See 'systemctl status proc-fs-nfsd.mount' for details.
[    9.421132] systemd[1]: Dependency failed for NFS server and services.
[DEPEND] Dependency failed for NFS server and services.
[    9.445149] systemd[1]: Dependency failed for NFS Mount Daemon.
[DEPEND] Dependency failed for NFS Mount Daemon.
[    9.465131] systemd[1]: nfs-mountd.service: Job nfs-mountd.service/start failed with result 'dependency'.
[    9.474740] systemd[1]: nfs-server.service: Job nfs-server.service/start failed with result 'dependency'.
[    9.486055] systemd[1]: Finished Remount Root and Kernel File Systems.
[  OK  ] Finished Remount Root and Kernel File Systems.
[    9.510800] systemd[1]: Finished Apply Kernel Variables.
[  OK  ] Finished Apply Kernel Variables.
[    9.536707] systemd[1]: Condition check resulted in FUSE Control File System being skipped.
[    9.548695] systemd[1]: Mounting Kernel Configuration File System...
         Mounting Kernel Configuration File System...
[    9.577816] systemd[1]: Condition check resulted in Rebuild Hardware Database being skipped.
[    9.586596] systemd[1]: Condition check resulted in Platform Persistent Storage Archival being skipped.
[    9.600088] systemd[1]: Starting Create System Users...
         Starting Create System Users...
[    9.621795] systemd[1]: Mounted Kernel Configuration File System.
[  OK  ] Mounted Kernel Configuration File System.
[    9.655677] systemd[1]: Finished Create System Users.
[  OK  ] Finished Create System Users.
[    9.681711] systemd[1]: Starting Create Static Device Nodes in /dev...
         Starting Create Static Device Nodes in /dev...
[    9.712112] systemd[1]: Finished Create Static Device Nodes in /dev.
[  OK  ] Finished Create Static Device Nodes in /dev.
[    9.733771] systemd[1]: Reached target Preparation for Local File Systems.
[  OK  ] Reached target Preparation for Local File Systems.
[    9.764949] systemd[1]: Mounting /var/volatile...
         Mounting /var/volatile...
[    9.785912] systemd[1]: Started Entropy Daemon based on the HAVEGE algorithm.
[  OK  ] Started Entropy Daemon based on the HAVEGE algorithm.
[    9.814289] systemd[1]: systemd-journald.service: unit configures an IP firewall, but the local system does not support BPF/cgroup firewalling.
[    9.841192] systemd[1]: (This warning is only shown for the first unit using IP firewalling.)
[    9.861766] systemd[1]: Starting Journal Service...
         Starting Journal Service...
[    9.890224] systemd[1]: Starting Rule-based Manager for Device Events and Files...
         Starting Rule-based Manage…for Device Events and Files...
[    9.925454] systemd[1]: Finished Coldplug All udev Devices.
[  OK  ] Finished Coldplug All udev Devices.
[    9.950013] systemd[1]: Mounted /var/volatile.
[  OK  ] Mounted /var/volatile.
[    9.965729] systemd[1]: Condition check resulted in Bind mount volatile /var/cache being skipped.
[    9.981239] systemd[1]: Condition check resulted in Bind mount volatile /var/lib being skipped.
[   10.005771] systemd[1]: Starting Load/Save Random Seed...
         Starting Load/Save Random Seed...
[   10.037477] systemd[1]: Condition check resulted in Bind mount volatile /var/spool being skipped.
[   10.054190] systemd[1]: Condition check resulted in Bind mount volatile /srv being skipped.
[   10.077468] systemd[1]: Reached target Local File Systems.
[  OK  ] Reached target Local File Systems.
[   10.137487] systemd[1]: Starting Rebuild Dynamic Linker Cache...
         Starting Rebuild Dynamic Linker Cache...
[   10.166829] systemd[1]: Started Rule-based Manager for Device Events and Files.
[  OK  ] Started Rule-based Manager for Device Events and Files.
[   10.194282] systemd[1]: Started Journal Service.
[  OK  ] Started Journal Service.
         Starting Flush Journal to Persistent Storage...
         Starting Network Configuration...
[   10.298514] systemd-journald[245]: Received client request to flush runtime journal.
[  OK  ] Finished Rebuild Dynamic Linker Cache.
[  OK  ] Finished Flush Journal to Persistent Storage.
         Starting Create Volatile Files and Directories...
[  OK  ] Finished Create Volatile Files and Directories.
         Starting Run pending postinsts...
         Starting Rebuild Journal Catalog...
         Starting Network Time Synchronization...
         Starting Record System Boot/Shutdown in UTMP...
[   11.062558] random: fast init done
[  OK  ] Finished Rebuild Journal Catalog.
[  OK  ] Finished Record System Boot/Shutdown in UTMP.
[  OK  ] Reached target Hardware activated USB gadget.
         Starting Update is Completed...
[   11.286242] update-alternatives: Linking /usr/lib/libMali.so.9.0 to /usr/lib/x11/libMali.so.9.0
[  OK  ] Finished Update is Completed.
[  OK  ] Started Network Configuration.
[   11.771453] macb ff0d0000.ethernet eth0: PHY [ff0d0000.ethernet-ffffffff:07] driver [Microchip KSZ9131 Gigabit PHY] (irq=POLL)
[   11.783012] macb ff0d0000.ethernet eth0: configuring for phy/rgmii-id link mode
[   11.800307] update-alternatives: Linking /usr/lib/libMali.so.9.0 to /usr/lib/x11/libMali.so.9.0
         Starting Network Name Resolution...
[  OK  ] Listening on Load/Save RF …itch Status /dev/rfkill Watch.
[   11.933919] pps pps0: new PPS source ptp0
[   11.965025] macb ff0d0000.ethernet: gem-ptp-timer ptp clock registered.
[   12.007617] Warn: update-alternatives: libmali-xlnx has multiple providers with the same priority, please check /usr/lib/opkg/alternatives/libmali-xlnx for details
[   12.207522] update-alternatives: Linking /usr/lib/libMali.so.9.0 to /usr/lib/x11/libMali.so.9.0
[  OK  ] Created slice Slice /system/systemd-fsck.
         Starting File System Check on /dev/mmcblk0p1...
[  OK  ] Found device /dev/mmcblk0p2.
[  OK  ] Finished File System Check on /dev/mmcblk0p1.
         Mounting /run/media/mmcblk0p1...
         Starting File System Check on /dev/mmcblk0p2...
[   12.706527] update-alternatives: Linking /usr/lib/libMali.so.9.0 to /usr/lib/x11/libMali.so.9.0
[   12.809168] random: crng init done
[   12.812598] random: 7 urandom warning(s) missed due to ratelimiting
[  OK  ] Finished Load/Save Random Seed.
[  OK  ] Mounted /run/media/mmcblk0p1.
[  OK  ] Finished File System Check on /dev/mmcblk0p2.
[  OK  ] Started Network Time Synchronization.
[  OK  ] Finished Run pending postinsts.
[  OK  ] Started Network Name Resolution.
[  OK  ] Reached target Network.
[  OK  ] Reached target Host and Network Name Lookups.
[  OK  ] Reached target System Initialization.
[  OK  ] Started Daily Cleanup of Temporary Directories.
[  OK  ] Reached target System Time Set.
[  OK  ] Reached target Timer Units.
[  OK  ] Listening on D-Bus System Message Bus Socket.
[  OK  ] Listening on dropbear.socket.
[  OK  ] Reached target Socket Units.
[  OK  ] Reached target Basic System.
         Mounting /run/media/mmcblk0p2...
[  OK  ] Started Kernel Logging Service.
[  OK  ] Started System Logging Service.
[  OK  ] Started D-Bus System Message Bus.
[  OK  ] Started NFS status monitor for NFSv2/3 locking..
         Starting LSB: NFS support for both client and server...
         Starting User Login Management...
         Starting Permit User Sessions...
         Starting Target Communication Framework agent...
[  OK  ] Started Xserver startup without a display manager.
[  OK  ] Started LSB: NFS support for both client and server.
[   14.385380] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null). Quota mode: none.
[  OK  ] Finished Permit User Sessions.
[  OK  ] Mounted /run/media/mmcblk0p2.
[  OK  ] Started Target Communication Framework agent.
[  OK  ] Started Getty on tty1.
         Starting inetd.busybox.service...
         Starting LSB: Kernel NFS server support...
[  OK  ] Started Serial Getty on ttyPS0.
[  OK  ] Reached target Login Prompts.
[  OK  ] Started inetd.busybox.service.
[FAILED] Failed to start LSB: Kernel NFS server support.
See 'systemctl status nfsserver.service' for details.
[  OK  ] Started User Login Management.
[  OK  ] Reached target Multi-User System.
[  OK  ] Reached target Graphical Interface.
         Starting Record Runlevel Change in UTMP...
[  OK  ] Finished Record Runlevel Change in UTMP.
[   14.977155] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[   14.982710] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 1
[   14.990565] xhci-hcd xhci-hcd.1.auto: hcc params 0x0238f625 hci version 0x100 quirks 0x0000000002010090
[   15.000056] xhci-hcd xhci-hcd.1.auto: irq 63, io mem 0xfe300000
[   15.006284] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 5.15
[   15.014585] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[   15.021828] usb usb1: Product: xHCI Host Controller
[   15.026714] usb usb1: Manufacturer: Linux 5.15.36-xilinx-v2022.2 xhci-hcd
[   15.033500] usb usb1: SerialNumber: xhci-hcd.1.auto
[   15.039013] hub 1-0:1.0: USB hub found
[   15.042804] hub 1-0:1.0: 1 port detected
[   15.047045] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[   15.052579] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 2
[   15.060291] xhci-hcd xhci-hcd.1.auto: Host supports USB 3.0 SuperSpeed
[   15.066981] usb usb2: We don't know the algorithms for LPM for this host, disabling LPM.
[   15.075294] usb usb2: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 5.15
[   15.083574] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[   15.090800] usb usb2: Product: xHCI Host Controller
[   15.095683] usb usb2: Manufacturer: Linux 5.15.36-xilinx-v2022.2 xhci-hcd
[   15.102477] usb usb2: SerialNumber: xhci-hcd.1.auto
[   15.107844] hub 2-0:1.0: USB hub found
[   15.111637] hub 2-0:1.0: 1 port detected

PetaLinux 2022.2_update1_04022314 zub1cgi4filterspeta ttyPS0

zub1cgi4filterspeta login: [   16.039529] macb ff0d0000.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
[   16.047274] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready

  1. 2023年10月02日 05:20 |
  2. ZUBoard
  3. | トラックバック:0
  4. | コメント:0
»