FC2カウンター FPGAの部屋

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

FPGAの部屋

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

RTLを語る会12でお話ししたスライドをSlideShare にアップロードしました

RTLを語る会(12) 〜Vivado HLS GO〜”でお話してきましたが、そのスライドをSlideShare にアップロードしました。
タイトルは”Vivado HLSのシミュレーションとHLSストリーム”です。

9ページの「Vivado HLSで書くのが難しい回路」の「スレッドが必要な場合」は、そう言えばやり方を教えてもらって(for 文にもDATAFLOW指示子が効くということ)やってみようと思っていたので、文を追加しました。物忘れが激しくていけません。

RTLを語る会12の関係者の皆さん、楽しい時を過ごさせて頂きました。また、よろしくお願いします。

注:HLSストリーム・ライブラリを使用する場合は、C ではなく、C++ を使ってください。
  1. 2016年09月26日 04:51 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

飛行ドローンを買ってみた

ドローンがどんなものか知りたくて、飛行ドローンを買ってみたいと思っていましたが、昨日アマゾンから購入して、届きました。

購入したドローンは、「Holy Stone iPhone&Androidで生中継可能 FPVリアルタイム伝送 ドローン 国内認証済み 日本語説明書 バッテリー 充電ケーブル2個付き モード1モード2自由転換 2.4GHz 4CH 6軸ジャイロ クアッドコプター X300c」です。
今は1万2千円くらいですが、タイムセールで 9,848 円でした。

できれば、飛行ドローンの自動運転もやってみたいですね。

届いた箱がこれです。
drone_1_160925.jpg

中身です。だいぶ外して作ってますけど。。。
drone_2_160925.jpg

コントローラについた黒いアダプタ部分にスマートフォンが付きます。

バッテリーは2つついているので安心ですね。2回飛ばせます。

家の中で飛ばそうと思ったのですが、コントローラに電池が 3 本必要なのですが、それが無かったんです。残念。。。
今日はRTLを語る会12 なので、今日も飛ばせないかな?
  1. 2016年09月25日 05:48 |
  2. ドローン
  3. | トラックバック:0
  4. | コメント:0

Zybot で Gabor filter を使うためのZYBO_0_5 プロジェクト3

Zybot で Gabor filter を使うためのZYBO_0_5 プロジェクト2”の続き。

前回は、、ハードウェアをエクスポートして、SDKを立ち上げ、アプリケーションソフトを作成して、ZYBO でテストしてうまく行った。これは、ベアメタル・アプリケーションだったので、今回は、BOOT.bin と devicetree.dtb を作成する。

まずは、SDKで FSBL を作成した。

FSBL プロジェクトで右クリックメニューから Create Boot Image を選択し、そうすると Create Boot Image ダイアログが表示されるので、uboot.elf を追加した。
ZYBO_0_5_DMA4G_14_160924.png

Create Image ボタンをクリックするとBOOT.bin が生成された。
ZYBO_0_5_DMA4G_15_160924.png

これで BOOT.bin ができたので、次は devicetree.dtb を作成する。
devicetree.dtb はLinux 上で作成するので、VirtualBox を起動して、zynq-zybo.dts を編集して、axi_vdma のエントリのところに下図の様に dmaw4gabor_0 のエントリを追加した。
ZYBO_0_5_DMA4G_16_160924.png

dtc -I dts -O dtb -o devicetree.dtb zynq-zybo.dts コマンドで zynq-zybo.dts をコンパイルして devicetree.dtb を作成した。
ZYBO_0_5_DMA4G_17_160924.png

これで、BOOT.bin と devicetree.dtb は作成できたので、次は、Linux上で動作するアプリケーション・ソフトを書き換える必要がある。
  1. 2016年09月25日 05:10 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Zybot で Gabor filter を使うためのZYBO_0_5 プロジェクト2

Zybot で Gabor filter を使うためのZYBO_0_5 プロジェクト1”の続き。

前回は、論理合成、インプリメント、ビットストリームの生成を行った。今回は、ハードウェアをエクスポートして、SDKを立ち上げ、アプリケーションソフトを作成して、ZYBO でテストする。

まずは、Vivado 2016.2 でハードウェアをエクスポートして、SDK を立ち上げた。

cam_disp3_axis.c を変更して、DMAW4Gabor IP に対応した。
gabor_disp, gabor_left, gabor_right プロジェクトも作成した。
ZYBO_0_5_DMA4G_10_160921.png

gabor_disp はガボール・フィルタを表示する設定を行い、左白線用のガボール・フィルタ画像を表示する。
gabor_right は右白線用のガボール・フィルタ画像を表示する。
gabor_left はガボール・フィルタを表示する設定無しで、左白線用のガボール・フィルタ画像を表示する。

画像の表示結果を示す。
まずはカメラ画像を示す。
ZYBO_0_5_DMA4G_11_160921.jpg

左白線用ガボール・フィルタ画像を示す。
ZYBO_0_5_DMA4G_12_160921.jpg

右白線用ガボール・フィルタ画像を示す。
ZYBO_0_5_DMA4G_13_160921.jpg

これで、白線検出のフレームレートを上げることが可能になると思う。
なぜかというと、 FRAME_BUFFER_ADDRESS番地からのアドレスには左白線検出用のガボール・フィルタ画像、 FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS番地からのアドレスには、右白線検出用のガボール・フィルタ画像がハードウェアによって自動的に更新される。よって、ソフトウェアではそれらの番地から白線を判定すれば良いわけだ。ガボール・フィルタを切り替える手間が省けるので、検出のフレームレートを上げることができると思う。

C ソースコードを貼っておく。
まずは、新しい cam_disp3_axis.c から。

// cam_disp3_axis.c
// 2015/12/02 by marsee
//
// Refered to Xilinx\SDK\2015.1\data\embeddedsw\XilinxProcessorIPLib\drivers\axivdma_v5_1\doc\html\api
// Refered to https://github.com/elitezhe/Atyls-VDMA-one-in-one-out/blob/master/SDK/colorbar/src/helloworld.c
// Refered to http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_2/pg020_axi_vdma.pdf
// Refered to http://forums.xilinx.com/t5/Embedded-Processor-System-Design/Axi-VDMA-on-Digilent-Atlys/td-p/297019/page/2
//
// normal camera out
//

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

#include "xdmaw4gabor.h"

#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    XDmaw4gabor xdma4g;

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    XDmaw4gabor_Initialize(&xdma4g, 0);
    XDmaw4gabor_Set_frame_buffer0(&xdma4g, FRAME_BUFFER_ADDRESS);
    XDmaw4gabor_Set_frame_buffer1(&xdma4g, FRAME_BUFFER_ADDRESS);
    XDmaw4gabor_Start(&xdma4g);
    XDmaw4gabor_EnableAutoRestart(&xdma4g);

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc0_axi_lites;
    volatile unsigned int *bmdc1_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc0_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    bmdc1_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_1_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc0_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 0 start
    bmdc1_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 1 start
    mt9d111_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    return(0);
}


gabor_disp.c を貼っておく。

/* * gabor_disp.c * *  Created on: 2016/09/23 *      Author: marsee */

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

#include "xgabor_filter_lh.h"
#include "xdmaw4gabor.h"

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

int main(){
    XGabor_filter_lh gabf;
    XDmaw4gabor xdma4g;

    XGabor_filter_lh_Initialize(&gabf, 0);
    XGabor_filter_lh_Start(&gabf);
    XGabor_filter_lh_EnableAutoRestart(&gabf);

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x48), 0x0); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x2);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // DMAW4Gabor Address set
    XDmaw4gabor_Initialize(&xdma4g, 0);
    XDmaw4gabor_Set_frame_buffer0(&xdma4g, FRAME_BUFFER_ADDRESS);
    XDmaw4gabor_Set_frame_buffer1(&xdma4g, FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS);

    // Bitmap display Controller Address set
    volatile unsigned int *bmdc0_axi_lites;
    volatile unsigned int *bmdc1_axi_lites;

    bmdc0_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    bmdc1_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_1_BASEADDR;

    bmdc0_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 0 start
    bmdc1_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller 1 start

    return 0;
}


gabor_right.c を貼っておく。

/* * gabor_right.c * *  Created on: 2016/09/23 *      Author: marsee */

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

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

int main(){
    volatile unsigned int *bmdc0_axi_lites;
    volatile unsigned int *bmdc1_axi_lites;

    bmdc0_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    bmdc1_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_1_BASEADDR;

    bmdc0_axi_lites[0] = (volatile unsigned int)(FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS); // Bitmap Display Controller 0 start
    bmdc1_axi_lites[0] = (volatile unsigned int)(FRAME_BUFFER_ADDRESS+ALL_DISP_ADDRESS); // Bitmap Display Controller 1 start

    return 0;
}


最後に、gabor_left.c を貼っておく。

/* * gabor_left.c * *  Created on: 2016/09/23 *      Author: marsee */

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

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

#define FRAME_BUFFER_ADDRESS 0x10000000
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

int main(){
    volatile unsigned int *bmdc0_axi_lites;
    volatile unsigned int *bmdc1_axi_lites;

    bmdc0_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    bmdc1_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_1_BASEADDR;

    bmdc0_axi_lites[0] = (volatile unsigned int)(FRAME_BUFFER_ADDRESS); // Bitmap Display Controller 0 start
    bmdc1_axi_lites[0] = (volatile unsigned int)(FRAME_BUFFER_ADDRESS); // Bitmap Display Controller 1 start

    return 0;
}

  1. 2016年09月23日 04:48 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Zybot で Gabor filter を使うためのZYBO_0_5 プロジェクト1

Zybot で Gabor filter を使う際のDMA Write IP”で作ったDMA Write IP とGabor_Filter_lh IP を使用することにする。ZYBO_0_3 プロジェクトをコピーしてZYBO_0_5 プロジェクトとした。ZYBO_0_5 プロジェクトのAXI VDMA をDMA Write IP と入れ替えた。さらに、Gabor_Filter_lh IP を更新した。

ZYBO_0_5 プロジェクトを示す。
ZYBO_0_5_DMA4G_1_160921.png

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

camera_interface モジュールを示す。
ZYBO_0_5_DMA4G_3_160921.png

Address Editor を示す。
ZYBO_0_5_DMA4G_4_160921.png

論理合成、インプリメント、ビットストリームの生成を行った。
ZYBO_0_5_DMA4G_5_160921.png

タイミングエラーが発生した。
インプリメント・デザインを起動して、原因を探す。
ZYBO_0_5_DMA4G_6_160921.png

clk_fpga0 の ラプラシアン・フィルタからガボール・フィルタへのパスがタイミングエラーになっていた。
ダブルクリックして、データ・パスのレポートを表示させる。
ZYBO_0_5_DMA4G_7_160921.png

ラプラシアン・フィルタから axis_switch_0 を通って、DMA Write IP を通り、ガボール・フィルタに行くパスがタイミングエラーのようだ。パスが長い、長すぎる。Vivado HLS でレジスタ・オプションを入れていないのが原因だろうが、一番手っ取り早いのは、真ん中辺りの axis_switch_0 にレジスタを入れられれば良いのではないだろうか?
そこで、axis_switch_0 をダブルクリックして、Pipeline Resisters -> Enable input pipeline register にチェックを入れた。
ZYBO_0_5_DMA4G_8_160921.png

これで、論理合成、インプリメント、ビットストリームの生成を再度行った。
するとタイミングエラーが無くなって、成功した。
ZYBO_0_5_DMA4G_9_160921.png
  1. 2016年09月22日 17:46 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Zybot で Gabor filter を使う際のDMA Write IP

Zybotのカメラによる白線追従走行”では、Gabor filter をソフトウェアで 250 ms ごとに切り替えていた。これは、500 ms に1回 Gabor filter の画像が取得できることを表している。この時間を短くしたいということで、AXI VDMA をVivado HLS 2016.2 で作成したDMA Write IP に差し替えようと思う。つまり、ハードウェアで自動的に左右のGabor filter を切り替えて、異なるアドレスのフレームバッファに書き込むことにしよう。

2016/09/22 修正: RorL を 1 ビットに修正しました)

DMA Write IP を書き換えて3 フレームバッファから 2 フレームバッファにした。次に、active_frame 信号の代わりに RorL 信号を追加した。
ヘッダファイルの dmaw4gabor.h を示す。これはシミュレーション用の画像用だ。実際に使用するときは、800 x 600 に変更する。なお、800 x 600 では、C/RTL協調シミュレーションがエラーになってしまった。現在の 64 x 48 では、問題なく C/RTL協調シミュレーションが通る。

// dmaw4gabor.h
// 2016/09/20 by marsee
//

#ifndef __DMA_WRITE_H__
#define __DMA_WRITE_H__

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

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    2

#endif


次に、dmaw4gabor.cpp を示す。

// dmaw4gabor.cpp
// 2016/09/20 by marsee
//
// frame_buffer0, frame_buffer1、2つのフレームバッファ
// if (RorL == 0) Left Gabor filter
// if (RorL == 1) Right Gabor filter
//

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

#include "dmaw4gabor.h"

int dmaw4gabor(hls::stream<ap_axis<32,1,1,1> >& ins, volatile int *out,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        volatile ap_uint<1> & RorL){
#pragma HLS INTERFACE ap_none port=RorL
#pragma HLS INTERFACE s_axilite port=frame_buffer0
#pragma HLS INTERFACE s_axilite port=frame_buffer1
#pragma HLS INTERFACE m_axi depth=5000000 port=out offset=off
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index;

    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0 :
                dma_index = frame_buffer0/sizeof(int);
                break;
            default//case 1 :
                dma_index = frame_buffer1/sizeof(int);
                break;
        }
        RorL = i;

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

        for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
            for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
                if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力
                out[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)+x] = pix.data;
            }
        }
    }
    return 0;
}


C シミュレーションは問題なかった。

C コードの合成も問題ない。C コードの合成結果は 800 x 600 に変更して行ったときに結果を示そうと思う。

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

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

ins_TVALID と ins_TREADY がずっと 1 で待ちがなく、連続的にAXI4-Stream でデータ転送ができていることがわかる。

次に dmaw4gabor.h の画像のサイズを 64 x 48 から 800 x 600 に変更して、C コードの合成を行った。結果を示す。
DMAW4Gabor_4_160921.png

問題なさそうなので、IP 化を行った。

2016/09/22 追加 : Gabor_Filter_lh を変更した)

DMA Write IP に接続するGabor filter も RorL の INTERFACE指示子を s_axilite から ap_none に変更した。今までは、AXI4 Lite Slave インターフェースのレジスタとして RorL がマップされていたが、出力ポートになった。
gabor_filter_lh_1_160922.png

C コードの合成をして、IP 化を行った。
  1. 2016年09月21日 04:16 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

memcpy() を利用したDMA Read IP 2(memcpy() が 2 個の場合)

memcpy() を利用したDMA Read IP 1(memcpy() が 1 個の場合)”の続き。

前回はDATAFLOW 指示子を使用して、memcpy() と for 文を重ね合わせて、約1クロックで 1 データを出力させることができた。ただし、カメラ画像表示システムに入れてテストしたところ、画像を表示することができなかった。
今回は、memcpy() を使用して、1行分の画像を転送するとエラーが発生する(DATAFLOW 指示子を入れない場合)ので、1 行の半分ずつ memcpy() するようにしてみた。さらに、そこにDATAFLOW 指示子を入れてみた。なお、Vivado HLS 2016.2 を使用している。
DMA_Read_addr.cpp を示す。

// DMA_Read_addr.cpp
// 2016/07/13 by marsee
//
// frame_buffer0, frame_buffer1, frame_buffer2 には3つのフレームバッファのアドレスを入れる
// mode = 0 : DMA Write IP の active_frame を見て、その1つ前のフレームをDMA Readするモード(DMA_WRITE_MODE)
// mode = 1 : フリーラン モード(FREE_RUN_MODE)
// 2016/09/18 : memcpy を使用
//

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

#include "DMA_Read.h"

int DMA_Read_addr(volatile int *in, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        unsigned int frame_buffer2, ap_uint<2> & active_frame,
        ap_uint<1> mode){
#pragma HLS INTERFACE s_axilite port=mode
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE s_axilite port=frame_buffer0
#pragma HLS INTERFACE s_axilite port=frame_buffer1
#pragma HLS INTERFACE s_axilite port=frame_buffer2
#pragma HLS INTERFACE m_axi depth=5000000 port=in offset=off
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index, n;
    int buf1[HORIZONTAL_PIXEL_WIDTH/2];
    int buf2[HORIZONTAL_PIXEL_WIDTH/2];

    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        if (mode == DMA_WRITE_MODE)
            n = (int)active_frame;
        else
            n = i;

        switch (n){ // 1つ前のフレームバッファを読みだす
            case 0 :
                dma_index = frame_buffer2/sizeof(int);
                break;
            case 1 :
                dma_index = frame_buffer0/sizeof(int);
                break;
            case 2 :
                dma_index = frame_buffer1/sizeof(int);
                break;
            default :
                dma_index = frame_buffer0/sizeof(int);
                break;
        } 

        for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
#pragma HLS DATAFLOW
            memcpy(buf1, (const int*)(&in[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)]), (HORIZONTAL_PIXEL_WIDTH)*2);
            memcpy(buf2, (const int*)(&in[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)+(HORIZONTAL_PIXEL_WIDTH/2)]),
                (HORIZONTAL_PIXEL_WIDTH)*2);
            for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
                if (x < HORIZONTAL_PIXEL_WIDTH/2)
                    pix.data = buf1[x];
                else
                    pix.data = buf2[x-HORIZONTAL_PIXEL_WIDTH/2];

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

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

                outs << pix;
            }
        }
    }

    return 0;
}


次に、DMA_Read.h を示す。

// DMA_Read.h
// 2016/07/14 by marsee
// 2016/09/18 : BURST_LENGTH を追加
//

#ifndef __DMA_READ_H__
#define __DMA_READ_H__

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600

//#define HORIZONTAL_PIXEL_WIDTH    64
//#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    3

#define DMA_WRITE_MODE    0
#define FREE_RUN_MODE    1

#define MEMCPY_LENGTH    (HORIZONTAL_PIXEL_WIDTH*4)

#endif


C シミュレーションを行ったところ、正常に画像をコピーすることができた。

次にC コードからの合成を行った。
DMA_Read_IP_test_46_160920.png

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

C/RTL協調シミュレーション波形を見た。
最初のAXI4 Master Read によるDMA 波形を示す。
DMA_Read_IP_test_48_160920.png

最初の番地は 0 番地のはずが、0x640 番地になっていた。これは、行の半分のピクセルのアドレスなので、2つ目の memcpy() のアクセスということになる。

次のAXI4 Master Read によるDMA 波形を示す。
DMA_Read_IP_test_49_160920.png

これも同様に、0x640 番地からスタートしている。これも、2つ目の memcpy() のアクセスということになる。つまり、2つ目の memcpy() のDMA 転送が 2 つ来てしまっている。
明らかにおかしい?

IP 化を行って、前回と同様に
Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム1(プロジェクト作成)
Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム2(SDK)
のDMA Read IP に入れ替えて、コンパイルし、実機で動作させてみた。
その結果、カメラ画像の右半分が 2 つ並んで、表示された。これは、そのようにDMA 転送しているので、正しい結果といえる。
DMA_Read_IP_test_45_160919.jpg

これはまずいように思うのだが。。。バグかな?

ちなみに、DATAFLOW 指示子をコメントアウトすると、C/RTL協調シミュレーションがエラーで停止してしまって比較ができない。
  1. 2016年09月20日 04:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»