FC2カウンター FPGAの部屋 2019年07月
FC2ブログ

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

FPGAの部屋

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

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する6(現状確認)

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する5(再度fpga.bin を生成)”の続き。

前回は、fpga.bin を再度生成した。今回はPcam5C カメラの画像をDisplayPort に出力するために現状を再確認しよう。

Pcam5C カメラ画像をFixstars Tech Blog さんの「Ultra96 Linux で MIPI カメラから画像を取得する (セットアップ編)」を参照して、Pcam5C カメラから PNG 画像を取得できた。それが、”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる3”だった。
ただし、Fixstars Tech Blog さんの「7. display port 経由での外部ディスプレイ出力」をやってみたところうまく行かなかった。”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる4”参照。

Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる3”をもう一度やってみた。
cd ~/example/Pcam5C/
sudo su
./init_camera.sh
cd test
./rgbtest

MIPI_DP_27_190718.png
MIPI_DP_28_190718.png

20 枚のカメラ画像が取得できた。
MIPI_DP_29_190718.png

rgbtest.cc を引用する。
MIPI_DP_30_190718.png

これで buf のアドレスを取得できれば良いな?と思ったが、仮想アドレスだし、物理アドレスに直しても連続しているとは限らないかも知れない?V4L2 も良く分からないので、ソフトウェアは自分で1から構築することにしようと思う。

現状で動作しているのが分かっているので、心強い。

VDMAの設定とかを書き足す予定です。続きます。
  1. 2019年07月19日 05:34 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する5(再度fpga.bin を生成)

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する4(再度ブロックデザインの変更)”の続き。

前回は、ultra96_design_dp ディレクトリのVivado 2018.2 プロジェクトのブロックデザインをdisp_dmar_axis_vga IP に入れ替えて、論理合成、インプリメンテーション、ビットストリームの生成を行った。今回は、fpga.bin を再度生成しよう。

fppga.bin を生成する。この作業は、”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる2”を参照してやっていこう。

~/Docker/vivado182ub16/masaaki/ultra96_design/ultra96_design_dp/ultra96_design.runs/impl_1/ ディレクトリに design_1_wrapper.bit があるのを確認した。
MIPI_DP_15_190710.png

同じディレクトリに fpga.bif を作成した。
MIPI_DP_16_190710.png

all:
{
    [destination_device = pl] design_1_wrapper.bit
}


MIPI_DP_17_190710.png

cd Docker/vivado182ub16/masaaki/ultra96_design/ultra96_design_dp/ultra96_design.runs/impl_1/
同じディレクトリで、
bootgen -image fpga.bif -arch zynqmp -w -o fpga.bin
を実行したところ、fpga.bin が生成された。
MIPI_DP_18_190710.png

MIPI_DP_19_190710.png

なお、ツィッターでAdam Taylor さんが”This week I am showing how to get the Zynq MPSoC DisplayPort controller up and running with live video from the Programmable Logic using Bare Metal SW”とツィートしていて、”MicroZed Chronicles: DisplayPort Controller — Part One”も公開されているので、少し待っていようと思う。
  1. 2019年07月18日 05:03 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する4(再度ブロックデザインの変更)

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する3(fpga.binファイルの生成)”の続き。

”Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する”シリーズも fpga.bin の生成まで行っているが、このままではまずい。なぜならば、VGA画像をXGA 画像やHD 画像に変換する必要がある。ということで、”VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 5(やっと完成2)”でVGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP を作成した。このDMA を使用するとVGA画像をXGA画像やHD画像に変換することができるので、”Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する2(ブロックデザインの変更)”のブロックデザインをdisp_dmar_axis_vga IP に入れ替えよう。

まずは、ultra96_design_dp ディレクトリのVivado 2018.2 プロジェクトを示す。
MIPI_DP_24_190717.png

ブロックデザインを示す。
MIPI_DP_21_190716.png

ultra96_design_dp ディレクトリにdisp_dmar_axis_vga_IP ディレクトリを作成し、”VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 5(やっと完成2)”で作成したIP のファイルをコピーした。
MIPI_DP_26_190717.png

IP Catalog にdisp_dmar_axis_vga を登録した。これで、disp_dmar_axis_vga を使用する準備が整った。
MIPI_DP_20_190716.png

ブロックデザインの中の display 階層モジュール内の disp_dmar_axis を削除して、disp_dmar_axis_vga に入れ替えた。
MIPI_DP_22_190716.png

Address Editor を示す。
MIPI_DP_23_190716.png

これで、論理合成、インプリメンテーション、ビットストリームの生成を行ったところ成功した。
Project Summary を示す。
MIPI_DP_25_190717.png
  1. 2019年07月17日 05:00 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 5(やっと完成2)

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 4(やっと完成1)”の続き。

前回は、風呂掃除をしていた時にふとひらめいた。いままで画像を拡張しない時はうまく行っていたdisp_dmar_axis IP があるのだから、それにHLSストリーム・インターフェースで画像を拡大するIP を付けたらどうか?そして、それらを並列化したら良いのでは?というアイデアだった。ということで、ソースコードを貼った。今回は、C シミュレーション、C コードの合成、C/RTL 協調シミュレーション、Export RTL を行った。

まずは、現在のdisp_dmar_axis_vga プロジェクトを示す。
disp_dmar_axis_vga_10_190715.png

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

dmar_result.bmp も問題ない。
disp_dmar_axis_vga_12_190715.png

disp_dmar_axis_vga_9_190713.jpg

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

これも良さそうだ。

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

Latency は 480036 クロックだった。これは、出力しているSVGA 画像の 800 x 600 = 480000 クロックとほぼ等しい。これは、良い結果が期待できそうだ。

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

m_axi_gmem_ARLEN を見ると、0f となっていて、16 バーストであることが分かる。
outs_TREADY と outs_TVALID もほとんど 1 固定だ。これは良い。

拡大した。
disp_dmar_axis_vga_17_190716.png

最後にExport RTL を行った。
disp_dmar_axis_vga_15_190715.png

CP achieved post-implementation は 3.305 ns で問題無さそうだ。

やはり、データを加工するDMA をVivado HLS で書く時は、シンプルなDMA してデータをHLSストリーム出力する関数と、HLSストリーム入出力のデータ加工関数を用意して、トップの関数でそれらを接続し、DATAFLOW 指示子で並列動作するように記述するのが賢いようだ。
  1. 2019年07月16日 04:59 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 4(やっと完成1)

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 3(2 種類の実装を試した)”の続き。

前回は、disp_dmar_axis IP 用のソースコードを 2 種類作って試してみたが、うまく行かなかった。今回は、風呂掃除をしていた時にひらめいたdisp_dmar_axis IP の実装を試してみよう。

風呂掃除をしていた時にふとひらめいた。いままで画像を拡張しない時はうまく行っていたdisp_dmar_axis IP があるのだから、それにHLSストリーム・インターフェースで画像を拡大するIP を付けたらどうか?そして、それらを並列化したら良いのでは?というアイデアだった。やはり、風呂掃除はアイデアが湧くのでお勧めである。皆さん、風呂掃除をしましょう。。。w

さて、今回はファイルも分けて、VGA画像を 3 チャネル分DMA するのが、vga_dmar_axis.cpp で、HLSストリーム・インターフェースで画像を拡大するIP が axis_expand.cpp (ちょっと名前をミスった)、全体をまとめるのが、disp_dmar_axis_vga.cpp となる。

disp_dmar_axis_vga.cpp のソースコードを示す。今回はインターフェースを変更して、row, col つまり画像の縦横のピクセル数を入力するようにした。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int vga_dmar_axis(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<2> active_frame);

int axis_expand(AXI_STREAM &ins, AXI_STREAM &outs, int row, int col);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<16> row, ap_uint<16> col, ap_uint<2> &active_frame){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE s_axilite port=row
#pragma HLS INTERFACE s_axilite port=col
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AXI_STREAM dmad;
#pragma HLS STREAM variable=dmad depth=64 dim=1

    vga_dmar_axis(fb0, fb1, fb2, dmad, active_frame);
    axis_expand(dmad, outs, row, col);
    
    return(0);
}


次に、vga_dmar_axis.cpp を示す。

// vga_dmar_axis.cpp
// 2019/07/13 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height);

int vga_dmar_axis(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<2> active_frame){

    AP_AXIU32 pix;

    if (active_frame == (ap_uint<2>)0)
        disp_dmar_fb2(fb2, outs, VGA_WIDTH, VGA_HEIGHT);
    else if (active_frame == (ap_uint<2>)1)
        disp_dmar_fb0(fb0, outs, VGA_WIDTH, VGA_HEIGHT);
    else
        disp_dmar_fb1(fb1, outs, VGA_WIDTH, VGA_HEIGHT);
    return(0);
}

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;

    LOOP_Y0: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        LOOP_X0: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb0[(y*max_width)+x];

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

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

            outs << pix;
        }
    }
    return(0);
}

int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;

    LOOP_Y1: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        LOOP_X1: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb1[(y*max_width)+x];

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

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

            outs << pix;
        }
    }
    return(0);
}

int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;

    LOOP_Y2: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        LOOP_X2: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb2[(y*max_width)+x];

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

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

            outs << pix;
        }
    }
    return(0);
}


axis_expand.cpp を示す。

// axis_expand.cpp
// 2019/07/13 by marsee
//


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

#include "disp_dmar_axis_vga.h"

int axis_expand(AXI_STREAM &ins, AXI_STREAM &outs, int row, int col){
    AP_AXIU32 pix;
    int x_padding = (col - VGA_WIDTH)/2;
    int y_padding = (row - VGA_HEIGHT)/2;

    for(int y=0; y<row; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        for(int x=0; x<col; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT){
                pix.data = 0;
                pix.dest = 0;
                pix.id = 0;
                pix.keep = 0;
                pix.strb = 0;
            }else if (x < x_padding || x >= x_padding+VGA_WIDTH){
                pix.data = 0;
                pix.dest = 0;
                pix.id = 0;
                pix.keep = 0;
                pix.strb = 0;
            }else{
                ins >> pix;
            }

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

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

            outs << pix;
        }
    }
    return(0);
}


前と同じだが、disp_dmar_axis_vga.h を示す。


// disp_dmar_axis_vga.h
// 2019/07/11 by marsee
//

#ifndef __DISP_DMAR_AXIS_VGA_H__
#define __DISP_DMAR_AXIS_VGA_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define VGA_WIDTH 640
#define VGA_HEIGHT 480

#define SVGA_WIDTH 800
#define SVGA_HEIGHT 600

#define XGA_WIDTH 1024
#define XGA_HEIGHT 768

#define HD_WIDTH 1920
#define HD_HEIGHT 1080

#define RESO_SVGA 0
#define RESO_XGA 1
#define RESO_HD  2

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef ap_axiu<32,1,1,1> AP_AXIU32;
typedef hls::Scalar<3, unsigned char> RGB_PIXEL;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC1> GRAY_IMAGE;

#endif


最後にbmp_file0.bmp だが、ブログに貼れないのでPNG ファイルを示す。
disp_dmar_axis_vga_8_190712.png
  1. 2019年07月15日 05:15 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 3(2 種類の実装を試した)

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 2(性能が足りない)”の続き。

前回は、disp_dmar_axis IP を作って、C シミュレーション、C コードの合成、C/RTL 協調シミュレーションを行ったが、C/RTL 協調シミュレーション波形から性能が足りないということが分かった。今回は、ソースコードを 2 種類書いて試してみたが、うまく行かなかった。

まずは、”2つのHLSストリームを同時に入力して演算してHLSストリーム出力2”を参考にして、DMA 部分とAXI4 Stream 部分を分けた。DMA部分では、VGA画像をDMA してHLS Stream で出力する。AXI4 Stream 部分では、DMA 部分からのAXI4 Stream を受けて、周りに 0 のデータを挿入して、解像度を大きくする。DMA 部分とAXI4 Stream 部分を別関数にして、DATAFLOW 指示子で並列動作を狙った。
ソースコード disp_dmar_axis_vga.cpp を示す。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_stream(AXI_STREAM &ins, AXI_STREAM &outs, int max_width, int max_height);
int fb0_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);
int fb1_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);
int fb2_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<16> row, ap_uint<16> col, ap_uint<2> active_frame){
#pragma HLS INTERFACE s_axilite port=col
#pragma HLS INTERFACE s_axilite port=row
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AP_AXIU32 pix;
    AXI_STREAM indata0;
    AXI_STREAM indata1;
    AXI_STREAM indata2;
#pragma HLS STREAM variable=indata0 depth=64 dim=1
#pragma HLS STREAM variable=indata1 depth=64 dim=1
#pragma HLS STREAM variable=indata2 depth=64 dim=1

    if (active_frame == (ap_uint<2>)0){
        fb2_hls_stream(fb2, indata0);
        disp_dmar_stream(indata0, outs, col, row);
    }else if (active_frame == (ap_uint<2>)1){
        fb0_hls_stream(fb0, indata1);
        disp_dmar_stream(indata1, outs, col, row);
    }else{
        fb1_hls_stream(fb1, indata2);
        disp_dmar_stream(indata2, outs, col, row);
    }

    return(0);
}

int disp_dmar_stream(AXI_STREAM &ins, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    AP_AXIU32 dmad;

    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    LOOP_Y0: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        LOOP_X0: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT)
                pix.data = 0;
            else if (x < x_padding || x >= x_padding+VGA_WIDTH)
                pix.data = 0;
            else{
                ins >> dmad;
                pix.data = dmad.data;
            }

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

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

            outs << pix;
        }
    }
    return(0);
}

int fb0_hls_stream(volatile ap_int<32> *fb0, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb0[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}

int fb1_hls_stream(volatile ap_int<32> *fb1, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb1[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}

int fb2_hls_stream(volatile ap_int<32> *fb2, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb2[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}


この実装でC コードの合成はうまく行くのだが、C/RTL 協調シミュレーションで出力が出ないで、シミュレーションがいつまで経っても終わらなかった。
いずれの問題も、DMA エンジンを 3 個入れているところがまずいのだと思うが、DMA アドレスのオフセットを毎回変えずに、トリプル・バッファリングを実現するのには、必要となる。


次の実装は、if 文での実装に問題が合って単発転送になっているのじゃないか?という推測のもとに、for 文を細かく区切って実装したら、性能的にはどうだろうか?ということでやってみた。
ソースコード disp_dmar_axis_vga.cpp を示す。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height);
void pix_in_zero(AP_AXIU32 &pix, int x, int y, int max_width);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<32> &resolution, ap_uint<2> &active_frame){
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE s_axilite port=resolution
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AP_AXIU32 pix;
    int max_width, max_height;

    switch((int)resolution){
        case 0: // SVGA
            max_width = SVGA_WIDTH;
            max_height = SVGA_HEIGHT;
            break;
        case 1: // XGA
            max_width = XGA_WIDTH;
            max_height = XGA_HEIGHT;
            break;
        default: // HD
            max_width = HD_WIDTH;
            max_height = HD_HEIGHT;
            break;
    }

    if (active_frame == (ap_uint<2>)0)
        disp_dmar_fb2(fb2, outs, max_width, max_height);
    else if (active_frame == (ap_uint<2>)1)
        disp_dmar_fb0(fb0, outs, max_width, max_height);
    else
        disp_dmar_fb1(fb1, outs, max_width, max_height);
    
    return(0);
}

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y00: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X00: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y01: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X010: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X011: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb0[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

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

            outs << pix;
        }
        X012: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y02: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X02: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y10: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X10: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y11: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X100: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X101: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb1[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

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

            outs << pix;
        }
        X102: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y12: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X12: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y20: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X20: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y21: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X210: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X211: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb2[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

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

            outs << pix;
        }
        X212: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y22: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X22: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

void pix_in_zero(AP_AXIU32 &pix, int x, int y, int max_width){
    pix.data = 0;
    
    if (x==0 && y==0)
        pix.user = 1;
    else
        pix.user = 0;

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


これも C/RTL 協調シミュレーションをやってみたところ、”VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 2(性能が足りない)”と同様に単発転送になってしまった。ここまで書くことは無いようだ。
次回は、うまく行った実装を紹介する。
  1. 2019年07月14日 05:06 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 2(性能が足りない)

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 1”の続き。

前回は、DisplayPort は接続するディスプレイによって、解像度の設定が違ってしまう。XGA のディスプレイだったらXGA 解像度の画像を出して、HD 解像度だったらHD 解像度の画像をLiveVideo に入力する必要がある。その加工はカメラ画像をDMA Write する側でやっていたのだが、今回のDMA Write はFixstars Tech Blog に書いてあるそのままを使用するので、DMA Read 側でいろいろな解像度の画像にすることにしよう。つまり、disp_dmar_axis IP を作り直そう。ということで、ソースコードやテストベンチのコードを貼った。
今回は、C シミュレーション、C コードの合成、をやってみよう。

Vivado 2018.2 でdisp_dmar_axis_vga プロジェクトを作成した。
disp_dmar_axis_vga_1_190712.png

最初に C シミュレーションを行った。
disp_dmar_axis_vga_2_190712.png

C シミュレーションの結果、dmar_result.bmp が生成された。予定通りの画像になっている。
disp_dmar_axis_vga_9_190713.jpg

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

Latency min が 480014 で、 SVGA の 800 x 600 = 480000 に比べて 12 クロックしか余計じゃないので、期待できそう?と思う。

disp_dmar_fb1 の合成結果を示す。
disp_dmar_axis_vga_4_190712.png

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

合成でのLatency min より、C/RTL 協調シミュレーションのLatency min の方がだいぶ増えてしまっている。

C/RTL 協調シミュレーション波形を見てみよう。全体波形から。
disp_dmar_axis_vga_6_190712.png

拡大してみよう。
disp_dmar_axis_vga_7_190712.png

outs に赤いところ(出力されていないところ)があるのが分かる。
m_axi_gmem_ARLENも 00 で単発転送であることが分かった。
これだとスループットが取れない。これではよろしくない。。。よって作り直そう。
  1. 2019年07月13日 22:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 1

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する3(fpga.binファイルの生成)”で、fpga.bin の生成まで行っておいて何なのだが、disp_dmar_axis IP がこのままではまずいことに気がついた。
DisplayPort は接続するディスプレイによって、解像度の設定が違ってしまう。XGA のディスプレイだったらXGA 解像度の画像を出して、HD 解像度だったらHD 解像度の画像をLiveVideo に入力する必要がある。その加工はカメラ画像をDMA Write する側でやっていたのだが、今回のDMA Write はFixstars Tech Blog に書いてあるそのままを使用するので、DMA Read 側でいろいろな解像度の画像にすることにしよう。つまり、disp_dmar_axis IP を作り直そう。

今度のDMA Read IP の名前は、disp_dmar_axis_vga とした。
最初にソースコードを貼っておく。
disp_dmar_axis_vga.h を貼っておく。

// disp_dmar_axis_vga.h
// 2019/07/11 by marsee
//

#ifndef __DISP_DMAR_AXIS_VGA_H__
#define __DISP_DMAR_AXIS_VGA_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define VGA_WIDTH 640
#define VGA_HEIGHT 480

#define SVGA_WIDTH 800
#define SVGA_HEIGHT 600

#define XGA_WIDTH 1024
#define XGA_HEIGHT 768

#define HD_WIDTH 1920
#define HD_HEIGHT 1080

#define RESO_SVGA 0
#define RESO_XGA 1
#define RESO_HD  2

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef ap_axiu<32,1,1,1> AP_AXIU32;
typedef hls::Scalar<3, unsigned char> RGB_PIXEL;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC1> GRAY_IMAGE;

#endif


disp_dmar_axis_vga.cpp を貼っておく。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<32> &resolution, ap_uint<2> &active_frame){
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE s_axilite port=resolution
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AP_AXIU32 pix;
    int max_width, max_height;

    switch((int)resolution){
        case 0: // SVGA
            max_width = SVGA_WIDTH;
            max_height = SVGA_HEIGHT;
            break;
        case 1: // XGA
            max_width = XGA_WIDTH;
            max_height = XGA_HEIGHT;
            break;
        default: // HD
            max_width = HD_WIDTH;
            max_height = HD_HEIGHT;
            break;
    }

    if (active_frame == (ap_uint<2>)0)
        disp_dmar_fb2(fb2, outs, max_width, max_height);
    else if (active_frame == (ap_uint<2>)1)
        disp_dmar_fb0(fb0, outs, max_width, max_height);
    else
        disp_dmar_fb1(fb1, outs, max_width, max_height);
    return(0);
}

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    LOOP_Y0: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        LOOP_X0: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT)
                pix.data = 0;
            else if (x < x_padding || x >= x_padding+VGA_WIDTH)
                pix.data = 0;
            else
                pix.data = fb0[((y-y_padding)*VGA_WIDTH)+x-x_padding];

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

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

            outs << pix;
        }
    }
    return(0);
}

int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    LOOP_Y1: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        LOOP_X1: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT)
                pix.data = 0;
            else if (x < x_padding || x >= x_padding+VGA_WIDTH)
                pix.data = 0;
            else
                pix.data = fb1[((y-y_padding)*VGA_WIDTH)+x-x_padding];

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

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

            outs << pix;
        }
    }
    return(0);
}

int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    LOOP_Y2: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        LOOP_X2: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT)
                pix.data = 0;
            else if (x < x_padding || x >= x_padding+VGA_WIDTH)
                pix.data = 0;
            else
                pix.data = fb2[((y-y_padding)*VGA_WIDTH)+x-x_padding];

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

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

            outs << pix;
        }
    }
    return(0);
}


disp_dmar_axis_vga_tb.cpp を貼っておく。

// disp_dmar_axis_tb.cpp
// 2019/02/01 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include "hls_opencv.h"

#include "disp_dmar_axis_vga.h"

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<32> &resolution, ap_uint<2> &active_frame);

#define NUM_FRAME_BUFFER 3
#define FB_NUMBER 0

int main(){
    using namespace cv;
    ap_uint<2> fb_number = FB_NUMBER;
    ap_uint<32> resolution = RESO_XGA;

    AXI_STREAM outs;
    AP_AXIU32 pix;
    ap_uint<32> *frame_buffer;
    
    // OpenCV で 画像を読み込む
    Mat src = imread("bmp_file0.bmp");

    // 画像をセーブするメモリをアロケート
    if ((frame_buffer =(ap_uint<32> *)malloc(NUM_FRAME_BUFFER * sizeof(ap_int<32>) * (src.cols * src.rows))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }
    
    // Mat フォーマットからフレームバッファに画像をロード
    Mat_<Vec3b> src_vec3b = Mat_<Vec3b>(src);
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        for(int y=0; y<src.rows; y++){
            for(int x=0; x<src.cols; x++){
                Vec3b pixel;
                pixel = src_vec3b(y,x);
                int rgb = ((int)(pixel(2))<<16) + ((int)(pixel(1))<<8) + pixel(0);
                frame_buffer[(src.cols*src.rows)*i + (src.cols*y) + x] = rgb;
            }
        }
    }
    
    disp_dmar_axis_vga((volatile ap_int<32> *)frame_buffer,
        (volatile ap_int<32> *)&frame_buffer[src.cols * src.rows],
        (volatile ap_int<32> *)&frame_buffer[2 * (src.cols * src.rows)],
        outs, resolution, fb_number);
        
    // AXI4 Stream から Mat フォーマットに変換しファイルに書き込み
    int row, col;
    switch(resolution){
        case RESO_SVGA:
            row = SVGA_HEIGHT; col = SVGA_WIDTH;
            break;
        case RESO_XGA:
            row = XGA_HEIGHT; col = XGA_WIDTH;
            break;
        default:
            row = HD_HEIGHT; col = HD_WIDTH;
            break;
    }

    Mat dst(row, col, CV_8UC3);
    Mat_<Vec3b> dst_vec3b = Mat_<Vec3b>(dst);
    for(int y=0; y<dst.rows; y++){
        for(int x=0; x<dst.cols; x++){
            outs >> pix;
            int rgb = pix.data;
            //int rgb = frame_buffer[(src.cols*y) + x];
            Vec3b pixel;
            pixel[0] = (rgb & 0xff); // blue
            pixel[1] = (rgb & 0xff00) >> 8; // green
            pixel[2] = (rgb & 0xff0000) >> 16; // red
            dst_vec3b(y,x) = pixel;
        }
    }

    imwrite("dmar_result.bmp", dst);
    
    free(frame_buffer);
    return(0);
}


bmp_file0.bmp を貼っておく。
disp_dmar_axis_vga_8_190712.png
  1. 2019年07月12日 04:53 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する3(fpga.binファイルの生成)

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する2(ブロックデザインの変更)”の続き。

前回は、Ultra96 にMIPI 拡張ボードを挿入し、Pcam5C を取り付けて、DisplayPort にPcam5C のカメラ画像を出力するVivado プロジェクトのブロックデザインを作成した。今回は、そのブロックデザインを論理合成、インプリメンテーション、ビットストリームの生成を行って、ビットファイルを生成させる。そして、そのビットファイルをbin ファイルに変換しよう。

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

タイミング制約も満足している。
しかし、55 個の critical warnings が出ているがこれは何だろうか?
critical warnings を見てみると、制約ファイルに書いてあるポートがデザインに無いと言うことのようだ。
MIPI_DP_14_190710.png

とりあえず、この 55 個の critical warnings は無視しよう。

次は、 fppga.bin を生成する。この作業は、”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる2”を参照してやっていこう。

~/Docker/vivado182ub16/masaaki/ultra96_design/ultra96_design_dp/ultra96_design.runs/impl_1/ ディレクトリに design_1_wrapper.bit があるのを確認した。
MIPI_DP_15_190710.png

同じディレクトリに fpga.bif を作成した。
MIPI_DP_16_190710.png

all:
{
    [destination_device = pl] design_1_wrapper.bit
}


MIPI_DP_17_190710.png

cd Docker/vivado182ub16/masaaki/ultra96_design/ultra96_design_dp/ultra96_design.runs/impl_1/
同じディレクトリで、
bootgen -image fpga.bif -arch zynqmp -w -o fpga.bin
を実行したところ、fpga.bin が生成された。
MIPI_DP_18_190710.png

MIPI_DP_19_190710.png
  1. 2019年07月10日 05:05 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する2(ブロックデザインの変更)

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する1(準備編)”の続き。

前回は、”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる4”でPcam5C のカメラ画像をディスプレイに表示することができなかった。だが、”カメラ画像をDisplayPortに出力する9(アプリを作成、完成)”でMIPI ではないパラレル出力のOV5642 をDisplayPort に表示することができている。そこで、両方を組み合わせてPcam5C の画像をUltra96 のDisplayPort に出力したいということで、PSのLiveVideo を有効にした。今回は、それを元にブロックデザインを完成させよう。

カメラ画像をDisplayPortに出力する7(ブロックデザインの変更)”を参考にして、ブロックデザインを変更しよう。

~/HDL/Ultra96/cam_dp_183/ ディレクトリから、axi2video_out_IP と disp_dmar_axis_IP ディレクトリをコピーした。
MIPI_DP_5_190709.png

それをVivado のプロジェクトのIP Catalog にリポジトリを登録した。
MIPI_DP_6_190709.png

ブロックデザイン上で、axi2video_out と disp_dmar_axis と axi_gpio を Add IP した。
MIPI_DP_7_190709.png

gpio の設定を示す。1 ビットの出力に設定した。
MIPI_DP_8_190709.png

Slice をAdd IP した。これは、axi_vdma の s2mm_frame_ptr_out の 6 ビットを 2 ビットに変換するに使用する。
MIPI_DP_9_190709.png

完成したブロックデザイン全体を示す。
MIPI_DP_10_190709.png

階層化した display 階層モジュールを示す。
MIPI_DP_11_190709.png

Address Editor 画面を示す。
MIPI_DP_12_190709.png
  1. 2019年07月09日 05:14 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Ultra96 MIPI拡張ボードに接続したPcam5C の画像をDisplayPort に表示する1(準備編)

Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる4”でPcam5C のカメラ画像をディスプレイに表示することができなかった。だが、”カメラ画像をDisplayPortに出力する9(アプリを作成、完成)”でMIPI ではないパラレル出力のOV5642 をDisplayPort に表示することができている。そこで、両方を組み合わせてPcam5C の画像をUltra96 のDisplayPort に出力したい。

まずは、”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる4”をやったときの ~/Docker/vivado182ub16/masaaki/ultra96_desgin/ultra96_design ディレクトリが Vivado 2018.2 のプロジェクトなので、それをコピーして、ultra96_design_dp と名前を変更した。
MIPI_DP_4_190708.png

Vivado 2018.2 でプロジェクトを開き、ブロックデザインを開いた。
MIPI_DP_2_190707.png

Ultra96のDisplayPortを使用するためのテスト1(Vivado プロジェクトを作成した)”に沿って、Live Video を 1 にして、DisplaryPort のポートを表示させた。
MIPI_DP_1_190707.png

Live Video を 1 にした後のブロックデザインを示す。
MIPI_DP_3_190707.png
  1. 2019年07月08日 04:53 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

映画『スパイダーマン:ファー・フロム・ホーム』を見てきました

今日は、午前中、トレラン教室で宝篋山に登ってきた後、映画『スパイダーマン:ファー・フロム・ホーム』を見てきました。
いつもながら、スパイダーマンは悩めるティーンエイジャーで、そこが良いでしょうが、歯がゆい気もしますね。。。
映画はとても楽しめましたよ。
  1. 2019年07月07日 21:54 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

hls4mlをやってみた7(自分で学習したネットワークをhls4mlでハードウェア化2)

hls4mlをやってみた6(自分で学習したネットワークをhls4mlでハードウェア化1)”の続き。

前回は、白線追従ロボットカーの学習データ keras_curve_cnn2_line を hls4ml でVivado HLS 2019.1 のプロジェクトにしようということで、白線追従ロボットカーの学習データのネットワーク構造と重みのファイルを出力した。今回は、それをhls4ml でVivado HLS のプロジェクトにしてみよう。

keras_curve_cnn2_line.json と keras_curve_cnn2_line_weights.h5 をhls4ml/keras-to-hls/example-keras-model-files/ ディレクトリにコピーした。
hls4ml_28_190704.png

hls4ml/keras-to-hls/ ディレクトリに keras_curve_cnn2_line.yml を作成した。
hls4ml_29_190704.png

keras_curve_cnn2_line.yml の内容を示す。
hls4ml_30_190704.png

KerasJson: example-keras-model-files/keras_curve_cnn2_line.json
KerasH5:   example-keras-model-files/keras_curve_cnn2_line_weights.h5
OutputDir: keras_curve_cnn2_line
ProjectName: keras_curve_cnn2_line
XilinxPart: xcku5p-sfvb784-1-e
ClockPeriod: 5

IOType: io_parallel # options: io_serial/io_parallel
HLSConfig:
  Model:
    Precision: ap_fixed<16,6>
    ReuseFactor: 1



hls4ml でVivado HLS の tcl ファイルを作成する。
python keras-to-hls.py -c keras_curve_cnn2_line.yml
hls4ml_31_190704.png

hls4ml/keras-to-hls/keras_curve_cnn2_line/ ディレクトリができて、その下に build_prj.tcl が生成されている。
hls4ml_32_190704.png

Vivado HLS で build_prj.tcl を実行したところエラーが出てしまった。
cd keras_curve_cnn2_line
vivado_hls -f build_prj.tcl

hls4ml_33_190704.png

ERROR: [XFORM 203-504] Stop unrolling loop 'Product1' (/home/masaaki/DNN/hls4ml/nnet_utils/nnet_dense.h:97) in function 'nnet::dense, ap_fixed<16, 6, (ap_q_mode)5, (ap_o_mode)3, 0>, config6>' because it may cause large runtime and excessive memory usage due to increase in code size. Please avoid unrolling the loop or form sub-functions for code in the loop body.
ERROR: [HLS 200-70] Pre-synthesis failed.
command 'ap_source' returned error code
while executing
"source build_prj.tcl"
("uplevel" body line 1)
invoked from within
"uplevel \#0 [list source $arg] "


large runtime and excessive memory usage エラーだった。このエラー、何とかならないものだろうか?何時間、何日かかっても良いので、続けて欲しい。Xilinx さん、よろしくお願いします。

一応、Vivado HLS 2019.1を立ち上げて、もう一度、C コードの合成をやってみたが結果は同じだった。
hls4ml_34_190705.png

hls4ml は小さいネットワークの時のみ、使えるみたいだ。
  1. 2019年07月06日 04:34 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

hls4mlをやってみた6(自分で学習したネットワークをhls4mlでハードウェア化1)

hls4mlをやってみた5(KERAS_conv1d_small_nfilt5その2)”の続き。

前回は、KERAS_conv1d_small_nfilt5 のネットワーク構造を調べた。今回は、白線追従ロボットカーの学習データ keras_curve_cnn2_line を hls4ml でVivado HLS 2019.1 のプロジェクトにしてみよう。

hls4ml にかけるには、モデルの構造を記述したJSON ファイルと、モデルの重みの h5 ファイルが必要なので、keras_curve_cnn2_line のJupyter notebook を立ち上げて、出力してみよう。
最初に学習済みモデルを読み込む。

# 学習済みモデルの読み込み

from keras.models import load_model

model = load_model('keras_curve_cnn2_line.h5')


hls4ml_25_190704.png

モデルを json に書き出す。

# save as JSON
json_string = model.to_json()


モデルを json ファイルに書き出して、重みも h5 ファイルに書き出す。

json_name='keras_curve_cnn2_line.json'

with open(json_name, mode='w') as f:
    f.write(json_string)

model.save_weights('keras_curve_cnn2_line_weights.h5')


hls4ml_26_190704.png

keras_curve_cnn2_line.json と keras_curve_cnn2_line_weights.h5 を出力することができた。
hls4ml_27_190704.png

なお keras のコードは、Keras Documentation の”モデルについて”を参考にさせていただいた。
  1. 2019年07月05日 05:00 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

hls4mlをやってみた5(KERAS_conv1d_small_nfilt5その2)

hls4mlをやってみた4(KERAS_conv1d_small_nfilt5その1)”の続き。

前回は、KERAS_conv1d_small_nfilt5 ネットワークをビルドして、C コードの合成結果を観察した。今回は、KERAS_conv1d_small_nfilt5 のネットワーク構造を調べていこう。

KERAS_conv1d_small_nfilt5.cpp を引用する。

//
//    rfnoc-hls-neuralnet: Vivado HLS code for neural-net building blocks
//
//    Copyright (C) 2017 EJ Kreinar
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#include <iostream>

#include "KERAS_conv1d_small_nfilt5.h"

//hls-fpga-machine-learning insert weights
#include "weights/w2.h"
#include "weights/b2.h"
#include "weights/w4.h"
#include "weights/b4.h"
#include "weights/w6.h"
#include "weights/b6.h"
#include "weights/w8.h"
#include "weights/b8.h"
#include "weights/w10.h"
#include "weights/b10.h"

void KERAS_conv1d_small_nfilt5(
    input_t input_1[N_INPUT_1_1*N_INPUT_2_1],
    result_t layer11_out[N_LAYER_10],
    unsigned short &const_size_in_1,
    unsigned short &const_size_out_1
) {

    //hls-fpga-machine-learning insert IO
    #pragma HLS ARRAY_RESHAPE variable=input_1 complete dim=0 
    #pragma HLS ARRAY_RESHAPE variable=layer11_out complete dim=0 
    #pragma HLS INTERFACE ap_vld port=input_1,layer11_out 
    #pragma HLS PIPELINE 

    const_size_in_1 = N_INPUT_1_1*N_INPUT_2_1;
    const_size_out_1 = N_LAYER_10;

    // ****************************************
    // NETWORK INSTANTIATION
    // ****************************************

    //hls-fpga-machine-learning insert layers

    layer2_t layer2_out[Y_OUTPUTS_2*N_FILT_2];
    #pragma HLS ARRAY_PARTITION variable=layer2_out complete dim=0
    nnet::conv_1d<input_t, layer2_t, config2>(input_1, layer2_out, w2, b2);

    layer3_t layer3_out[Y_OUTPUTS_2*N_FILT_2];
    #pragma HLS ARRAY_PARTITION variable=layer3_out complete dim=0
    nnet::relu<layer2_t, layer3_t, relu_config3>(layer2_out, layer3_out);

    layer4_t layer4_out[Y_OUTPUTS_4*N_FILT_4];
    #pragma HLS ARRAY_PARTITION variable=layer4_out complete dim=0
    nnet::conv_1d<layer3_t, layer4_t, config4>(layer3_out, layer4_out, w4, b4);

    layer5_t layer5_out[Y_OUTPUTS_4*N_FILT_4];
    #pragma HLS ARRAY_PARTITION variable=layer5_out complete dim=0
    nnet::relu<layer4_t, layer5_t, relu_config5>(layer4_out, layer5_out);

    layer6_t layer6_out[Y_OUTPUTS_6*N_FILT_6];
    #pragma HLS ARRAY_PARTITION variable=layer6_out complete dim=0
    nnet::conv_1d<layer5_t, layer6_t, config6>(layer5_out, layer6_out, w6, b6);

    layer7_t layer7_out[Y_OUTPUTS_6*N_FILT_6];
    #pragma HLS ARRAY_PARTITION variable=layer7_out complete dim=0
    nnet::relu<layer6_t, layer7_t, relu_config7>(layer6_out, layer7_out);

    layer8_t layer8_out[N_LAYER_8];
    #pragma HLS ARRAY_PARTITION variable=layer8_out complete dim=0
    nnet::dense<layer7_t, layer8_t, config8>(layer7_out, layer8_out, w8, b8);

    layer9_t layer9_out[N_LAYER_8];
    #pragma HLS ARRAY_PARTITION variable=layer9_out complete dim=0
    nnet::relu<layer8_t, layer9_t, relu_config9>(layer8_out, layer9_out);

    layer10_t layer10_out[N_LAYER_10];
    #pragma HLS ARRAY_PARTITION variable=layer10_out complete dim=0
    nnet::dense<layer9_t, layer10_t, config10>(layer9_out, layer10_out, w10, b10);

    nnet::softmax<layer10_t, result_t, softmax_config11>(layer10_out, layer11_out);


}


KERAS_conv1d_small_nfilt5.cpp と parameters.h から推定した層構成を示す。ただしすべての層の変数の型は ap_fixed<16,6> となっている。

入力層 40 入力
conv_1d 入力数 40 出力数 30
relu 入力数 30 出力数 30
conv_1d 入力数 30 出力数 20
relu 入力数 20 出力数 20
conv_1d 入力数 20 出力数 10
relu 入力数 10 出力数 10
dense 入力数 10 出力数 5
relu 入力数 5 出力数 5
dense 入力数 5 出力数 5
softmax 入力数 5 出力数 5


IP の入力と出力のフォーマットだが、 KERAS_conv1d_small_nfilt5.cpp で dim=0 で ARRAY_RESHAPE ディレクティブが書かれている。
よって、入力の input_1 が ap_fixed<16,6> が 40 個なので、640 ビット幅となり、
出力の layer9_out が ap_fixed<16,6> が 5 個なので、 80 ビット幅となっている。
KERAS_conv1d_small_nfilt5.vhd の entity 部分を引用する。

entity KERAS_conv1d_small_nfilt5 is
port (
    ap_clk : IN STD_LOGIC;
    ap_rst : IN STD_LOGIC;
    ap_start : IN STD_LOGIC;
    ap_done : OUT STD_LOGIC;
    ap_idle : OUT STD_LOGIC;
    ap_ready : OUT STD_LOGIC;
    input_1_V_ap_vld : IN STD_LOGIC;
    input_1_V : IN STD_LOGIC_VECTOR (639 downto 0);
    layer11_out_V : OUT STD_LOGIC_VECTOR (79 downto 0);
    layer11_out_V_ap_vld : OUT STD_LOGIC;
    const_size_in_1 : OUT STD_LOGIC_VECTOR (15 downto 0);
    const_size_in_1_ap_vld : OUT STD_LOGIC;
    const_size_out_1 : OUT STD_LOGIC_VECTOR (15 downto 0);
    const_size_out_1_ap_vld : OUT STD_LOGIC );
end;


2つの hls4ml のexample ネットワークを見てきたが、小さいネットワークだった。自分で構築したニューラルネットワークを次にはやってみたい。
  1. 2019年07月04日 04:56 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

hls4mlをやってみた4(KERAS_conv1d_small_nfilt5その1)

hls4mlをやってみた3(シミュレーション)”の続き。

前回は、ivado HLS プロジェクトの myproject_prj の C シミュレーションとC/RTL 協調シミュレーションの結果を見た。今回は、異なるネットワークを自分で yml ファイルを作成してやってみよう。

hls4ml/keras-to-hls/example-keras-model-files/ ディレクトリのKERAS_conv1d_small_nfilt5.json , KERAS_conv1d_small_nfilt5_weights.h5 の組のネットワークをVivado HLS で実装してみることにする。
hls4ml_17_190703.png

hls4ml/keras-to-hls/ ディレクトリに KERAS_conv1d_small_nfilt5.yml を作成した。
hls4ml_18_190703.png

KERAS_conv1d_small_nfilt5.yml の内容を示す。
hls4ml_19_190703.png

KerasJson: example-keras-model-files/KERAS_conv1d_small_nfilt5.json
KerasH5: example-keras-model-files/KERAS_conv1d_small_nfilt5_weights.h5
OutputDir: KERAS_conv1d_small_nfilt5
ProjectName: KERAS_conv1d_small_nfilt5
XilinxPart: xcku5p-sfvb784-1-e
ClockPeriod: 5

IOType: io_parallel # options: io_serial/io_parallel
HLSConfig:
Model:
Precision: ap_fixed<16,6>
ReuseFactor: 1


hls4ml でKERAS_conv1d_small_nfilt5 のネットワークを Vivaod HLS のプロジェクトへ変換した。
python keras-to-hls.py -c KERAS_conv1d_small_nfilt5.yml
hls4ml_20_190703.png

KERAS_conv1d_small_nfilt5 ディレクトリに行って、build_prj.tcl を実行する。
cd KERAS_conv1d_small_nfilt5
vivado_hls -f build_prj.tcl

hls4ml_21_190703.png

tcl スクリプトの実行が終了した。
hls4ml_22_190703.png

Vivado HLS 2019.1 を立ち上げて、C コードの合成の結果を確認した。
hls4ml_23_190703.png
hls4ml_24_190703.png

Latency は 15 クロックで、Interval は 1 クロックだった。
リソースも今度はFPGA に収まっている。
  1. 2019年07月03日 05:10 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

hls4mlをやってみた3(シミュレーション)

hls4mlをやってみた2(KERAS_3layer ネットワークの構成)”の続き。

前回は、Vivado HLS を使用して機械学習をハードウェア化するフレームワークのhls4ml を使用してVivado HLS のIP として生成した KERAS_3layer ネットワークがどのようななネットワークか知らないので調査した。今回は、そのVivado HLS プロジェクトの myproject_prj の C シミュレーションとC/RTL 協調シミュレーションの結果を見ていこう。

最初にテストベンチの myproject_test.cpp を引用する。

//
//    rfnoc-hls-neuralnet: Vivado HLS code for neural-net building blocks
//
//    Copyright (C) 2017 EJ Kreinar
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "firmware/parameters.h"
#include "firmware/myproject.h"
#include "nnet_helpers.h"


int main(int argc, char **argv)
{
  //hls-fpga-machine-learning insert data
  input_t input_1[N_INPUT_1_1] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  result_t layer9_out[N_LAYER_8] = {0,0,0,0,0};

  //hls-fpga-machine-learning insert top-level-function
  unsigned short size_in1,size_out1;
  myproject(input_1,layer9_out,size_in1,size_out1);

  //hls-fpga-machine-learning insert output
  for(int i = 0; i < N_LAYER_8; i++) {
    std::cout << layer9_out[i] << " ";
  }
  std::cout << std::endl;

  return 0;
}


入力配列(input_1)に値を代入して、1度だけ myproject() を読んでいる。
このテストベンチでのシミュレーション結果を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../../myproject_test.cpp in debug mode
   Compiling ../../../../firmware/myproject.cpp in debug mode
   Generating csim.exe
0.0292969 0.761719 0.0566406 0.142578 0.0361328 
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


layer9_out[] に値が出力されいてる。

次に、C/RTL 協調シミュレーションの結果を示す。
hls4ml_14_190701.png

Latency は 15 クロックだが、Interval が NA になってしまっている。これは、myproject() のコールが 1 回のためだと思う。
それでは、テストベンチの myproject_test.cpp を myproject() を 2 回呼ぶように書き換えた。新しい myproject_test.cpp を示す。

//
//    rfnoc-hls-neuralnet: Vivado HLS code for neural-net building blocks
//
//    Copyright (C) 2017 EJ Kreinar
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "firmware/parameters.h"
#include "firmware/myproject.h"
#include "nnet_helpers.h"


int main(int argc, char **argv)
{
  //hls-fpga-machine-learning insert data
  input_t input_1[N_INPUT_1_1] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  input_t input_2[N_INPUT_1_1] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  result_t layer9_out[N_LAYER_8] = {0,0,0,0,0};
  result_t layer9_out2[N_LAYER_8] = {0,0,0,0,0};

  //hls-fpga-machine-learning insert top-level-function
  unsigned short size_in1,size_out1;
  myproject(input_1,layer9_out,size_in1,size_out1);
  myproject(input_2,layer9_out2,size_in1,size_out1);

  //hls-fpga-machine-learning insert output
  for(int i = 0; i < N_LAYER_8; i++) {
    std::cout << layer9_out[i] << " ";
  }
  std::cout << std::endl;

  for(int i = 0; i < N_LAYER_8; i++) {
    std::cout << layer9_out2[i] << " ";
  }
  std::cout << std::endl;

  return 0;
}


このテストベンチで C シミュレーションを行った。結果を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../../myproject_test.cpp in debug mode
   Generating csim.exe
0.0292969 0.761719 0.0566406 0.142578 0.0361328 
0 0.399414 0.0439453 0.571289 0.015625 
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


結果が 2 つになった。

C/RTL 協調シミュレーションの結果を示す。
hls4ml_15_190701.png

Interval が 1 になった。
C/RTL 協調シミュレーションの波形を確認しよう。
hls4ml_16_190701.png

波形を少し拡大してみよう。
hls4ml_17_190701.png

input_1_V_ap_vld が 1 になったクロックから layer9_out_V_ap_vld が 1 になるまでは、 75 ns で クロックが 5 ns なので、15 クロックかかっている。そして、次の結果が 1 クロック後に出いているので、Latency は 15 クロック、Interval が 1 クロックということがC/RTL 協調シミュレーションの波形からも確認することができた。
  1. 2019年07月02日 04:58 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

靴棚を作った

土曜日と日曜日に奥さんの要望で天井まで届く靴棚を作った。

2730 mm X 800 mm X 240 mm の靴棚なので、どうやって作るか迷ったが、高さ方向に 1530 mm と 1200 mm に分けて作ることにした。材料は 1 x 4 材 6F を 24 本買ってきた。約 6000 円だった。

まずは、下半分の 1530 mm を作った。
kutudana_1_190701.jpg

上半分を作っているところ。作業台の上に 1 x 4 材で作った靴棚の上半分のそのまた半分が載っている。もう半分を作って 240 mm の 1 x 4 材で連結する。こうして、 1 x 4 材で作ると安くなるのである。そして、重さも一人で持てる程度である。全部このサイズをムク材で作ったら一人で持てなくなる。この作り方でも、半分に分けて、やっと一人で持てる重さになる
kutudana_2_190701.jpg

作業場には、安いがスライドノコギリも用意してある。これが無いとまっすぐ垂直に材木を切れない。
kutudana_3_190701.jpg

最近あまりやっていないが、木工も趣味の1つなのだ。

玄関で上下を連結して出来上がり。しかし、一人で設置だったので、上半分が少し曲がってしまったかな?まあ、天井に押し付けて止めたので、天井に突っ張っているので、手でやっても動かないので、大丈夫だろう。
kutudana_4_190701.jpg

kutudana_5_190701.jpg

kutudana_6_190701.jpg

CNC買って家具作りのアルバイトも良いかも?
  1. 2019年07月01日 04:56 |
  2. 木工
  3. | トラックバック:0
  4. | コメント:0