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

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

FPGAの部屋

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

Viavdo HLS の array_FIFO Example

Xilinx 社のフォーラムの”Vivado HLS - AXI4 Stream FIFO”を見ていたら、Vivao HLS のExample として、array_FIFO があるというのがわかりました。
fifo_test_20_160930.png

array_FIFO プロジェクトです。
fifo_test_21_160930.png

C コードの合成をすると、BRAMのインターフェースが出ていて、BRAM外付けです。
fifo_test_22_160930.png

このコードでも書けることがわかりましたが、やはり i が 0 から 3 まですね。
  1. 2016年09月30日 05:07 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS でFIFO を作ってみた2(指示子を入れた場合)

Vivado HLS でFIFO を作ってみた1(指示子を入れない場合)”の続き。

前回は、入出力数に制限のあるFIFO を作ってみたが、DATAFLOW 指示子やPIPELINE 指示子を入れてないので、input のfor 文とooutput のfor 文がシーケンシャルに実行されてしまったし、output は出力するのに 2 クロックかかってしまった。今回は、DATAFLOW 指示子とPIPELINE 指示子を入れて、どう変わるのか?を検証してみよう。

指示子を入れた fifo_test.cpp を示す。

// fifo_test.cpp
// 2016/09/26 by marsee
//

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

#include "fifo_test.h"

int fifo_test(hls::stream<ap_uint<32> >& ins, hls::stream<ap_uint<32> >& outs){
#pragma HLS DATAFLOW
    ap_uint<32> buf[HORIZONTAL_PIXEL_WIDTH];
    ap_uint<32> ind, outd;
    int i, j;

    input : for(i=0; i<HORIZONTAL_PIXEL_WIDTH; i++){
#pragma HLS PIPELINE II=1
        ins >> buf[i];
    }

    output : for(j=0; j<HORIZONTAL_PIXEL_WIDTH; j++){
#pragma HLS PIPELINE II=1
        while(i <= j) ; // i よりも j が等しいか大きい場合はwait
        outs << buf[j];
    }

    return 0;
}


input にはPIPELINE 指示子はいらないかもしれないが、一応入れておいた。

C コードの合成の結果を示す。今回の結果を左側に、前回の結果を右側に示す。
fifo_test_9_160929.png fifo_test_2_160927.png

Latency は 643 クロックになっていて、input と output が並列に実行されていることがわかる。
Loop がN/A になっていて、FF も LUT も増えている。

Analysis にしてみた。
fifo_test_10_160929.png

fifo_test_Loop_input_proc と fifo_test_Loop_output_proc があった。
そのうちのfifo_test_Loop_input_proc をクリックした。
fifo_test_11_160929.png

戻って、 fifo_test_Loop_output_proc をクリックした。
fifo_test_12_160929.png

Resource 表示にした。
fifo_test_13_160929.png

fifo_test_Loop_input_proc_U0 と fifo_test_Loop_output_proc_U0 があった。
そのうちのfifo_test_Loop_input_proc_U0 をクリックした。
fifo_test_14_160929.png

戻って、fifo_test_Loop_output_proc_U0 をクリックした。
fifo_test_15_160929.png

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

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

fifo_test() 呼び出し 1 回分を示す。
fifo_test_18_160929.png

ins_V_empty_n, ins_V_V_read, outs_V_V_full_n, outs_V_V_write も呼び出しの間は1 を保持しているので、1 クロックで input と output の処理を同時に行うことができている。

次に、入力と出力が何クロック遅延しているかを見てみよう。
fifo_test_19_160929.png

図から入力データと出力データは 2 クロック遅れているようだ。

これで、Vivado HLS でも、FIFO が作れることが分かった。
  1. 2016年09月29日 04:37 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS でFIFO を作ってみた1(指示子を入れない場合)

Vivado HLS でFIFO を作ってみようと思う。
DATAFLOW 指示子が for 文にも有効ということが分かったので、それを試してみよう。最初はPIPELINE や DATAFLOW 指示子を入れないでどうなるのかを試してから入れてみようと思う。

さて、作成する FIFO は、画像表示の 1 行分を保持する FIFO を作ろうと思う。この FIFO は 1 行分の配列に入力ピクセルを入れて、配列から出力するものとする。画像の 1 行は、ピクセルデータが出力される画像表示部分と画像が表示されないフロント・ポーチ、水平同期信号、バック・ポーチがあるので、次の行のデータ処理のことは考えなくても済むので、それを考慮した実装にした。

最初に、fifo_test.h を示す。

// fifo_test.h
// 2016/09/26 by marsee
//

#ifndef __FIFO_TEST_H__
#define __FIFO_TEST_H__

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

#define HORIZONTAL_PIXEL_WIDTH 640
#define VERTICAL_PIXEL_WIDTH 480

#endif


fifo_test.cpp を示す。まだ、指示子を入れていない状態だ。

// fifo_test.cpp
// 2016/09/26 by marsee
//

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

#include "fifo_test.h"

int fifo_test(hls::stream<ap_uint<32> >& ins, hls::stream<ap_uint<32> >& outs){
    ap_uint<32> buf[HORIZONTAL_PIXEL_WIDTH];
    ap_uint<32> ind, outd;
    int i, j;

    input : for(i=0; i<HORIZONTAL_PIXEL_WIDTH; i++){
        ins >> buf[i];
    }

    output : for(j=0; j<HORIZONTAL_PIXEL_WIDTH; j++){
        while(i <= j) ; // i よりも j が等しいか大きい場合はwait
        outs << buf[j];
    }

    return 0;
}


テストベンチは、BMPファイルを読み込んで、fifo_test() を通したデータをもう一度、result_copy.bmp に書き込む。
テストベンチの fifo_test_tb.cpp を示す。

// fifo_test_tb.cpp
// 2016/09/26 by marsee
//

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

#include "fifo_test.h"
#include "bmp_header.h"

int fifo_test(hls::stream<ap_uint<32> >& ins, hls::stream<ap_uint<32> >& outs);

#define BMP_FILE_NAME   "road_1.bmp"

int main()
{
    using namespace std;

    hls::stream<ap_uint<32> > ins;
    hls::stream<ap_uint<32> > outs;
    ap_uint<32> pix, result;

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

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

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

    if ((result_copy =(ap_uint<32> *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate result_copy 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);

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            pix = rd_bmp[(j*bmpihr.biWidth)+i];
            ins << pix;
        }
    }

    // 1行容量の fifo_test を呼び出しながら、malloc 領域に書き込む
    for(int j=0; j < bmpihr.biHeight; j++){
        fifo_test(ins, outs);

        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> result;
            result_copy[(j*bmpihr.biWidth)+i] = result;
        }
    }

    // ハードウェアのラプラシアンフィルタの結果を result_copy.bmp へ出力する
    if ((fbmpw=fopen("result_copy.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open result_copy.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpw);
    fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
    // RGB データの書き込み、逆順にする
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

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

    free(rd_bmp);
    free(result_copy);
}


Vivado HLS 2016.2 の fifo_test プロジェクトを示す。
fifo_test_1_160927.png

C シミュレーションは成功した。result_copy.bmp も road_1.bmp と同じだった。

次に、C コードの合成を行った。
fifo_test_2_160927.png

loop を見ると、input は指示子を入れて無くてもIteration Latency が 1 だが、output はIteration Latency が 2 になっている。

Analysis を見てみた。input と output の処理がよくわかる。
fifo_test_3_160927.png

Analysis でResource 画面にしてみよう。buf_V へのトランザクションがよくわかる。
fifo_test_4_160927.png

C/RTL協調シミュレーションを行った。1922 クロックだった。これは1行分だ。
fifo_test_5_160927.png

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

HLSストリームのデフォルトのIOレベルのプロトコルは ap_fifo になっているようだ。

波形を拡大して、1行分の画像のFIFO 処理を表示した。
fifo_test_7_160927.png

ins_V_V_read は1 になったら、1 のままで、1クロックごとに処理ができているのがわかる。
outs_V_V_write は1 になっても 0 になることがあるようだ。

outs_V_V_write がどうなっているのか?を見るために、波形を拡大した。
fifo_test_8_160927.png

outs_V_V_write は、合成レポートにもあったように、1クロックごとに 1 と 0 を繰り返している。つまり、2クロックで出力しているということになる。
  1. 2016年09月28日 04:41 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

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. | コメント:5

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. Zybot
  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

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

Vivado HLS で DMA Read IP を作る(絶対アドレス指定版)”は論理シミュレーションでは動作するが、論理合成すると動作しなくなってしまう。

それでは、ということで、DMA Read IP の異なる実装を試してみることにした。Vivado HLS 2016.2 を使用している。
memcpy() を使用して明示的にAXI4 のバースト転送を使用する。
DMA_Read_IP_test_39_160919.png

最初に

memcpy(buf1, (const int*)(&in[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)]), (HORIZONTAL_PIXEL_WIDTH)*4);

で1行分の画像データをRead しようとしたが、これだと、Cコードからの合成で不思議なエラーになってしまう。

Instruction does not dominate all uses!
%in_addr = getelementptr inbounds i32* %in, i64 %tmp_12_mid2, !dbg !1886
%in_addr_1_rd_req = call i1 @_ssdm_op_ReadReq.m_axi.i32P(i32* %in_addr, i32 480000), !dbg !1886
Broken module found, compilation aborted!
R6025
- pure virtual function call


また、不思議なことに、これにDATAFLOW指示子を追加すると、Cコードからの合成が通るようになった。
DMA_Read_IP_test_40_160919.png

Latency が min, max 同じ値で 1462215 だった。
今、800 x 600 ピクセルの画像を 3 回処理しているので、 800 x 600 x 3 = 1440000 なので、1462215 / 1440000 ≒ 1.015 クロック/ピクセルとなっているので、とっても良い具合だ。

DATAFLOW 指示子は関数の並列化だけではなく、for 文にも効くということだった。(kenichio0402さんと同僚の方、ありがとうございます)
なお、UG902 の128ページの「タスクレベルのパイプライン : データ フ ロー最適化」に最適化の例がある。

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

1467869クロックだった。

C/RTL協調シミュレーション波形を示す。連続的にAXI4 Master Read アクセスが出ている。
DMA_Read_IP_test_42_160919.png

DMA_Read_IP_test_43_160919.png

IP 化を行って、
Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム1(プロジェクト作成)
Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム2(SDK)
のDMA Read IP に入れ替えて、コンパイルし、実機で動作させてみた。
その結果、ランダム画面が下に表示されたっきりで、カメラ画像は表示できなかった。
DMA_Read_IP_test_44_160919.jpg

カメラ画像システムが悪いのか?それともDMA Read IP が悪いのか?
カメラ画像システムは、他のDMA Read IP で正常とは言えないまでも、映っているので、DMA Read IP が悪いのかもしれない?

現在の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 buf[HORIZONTAL_PIXEL_WIDTH];

    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(buf, (const int*)(&in[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)]), (HORIZONTAL_PIXEL_WIDTH)*4);
            for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
                pix.data = buf[x];

                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;
}

  1. 2016年09月19日 10:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

DMA Read IP を単体でシミュレーション4(結論)

DMA Read IP を単体でシミュレーション3(DMA Read IP単体で論理合成後にシミュレーション)”の続き。

結局、DMA_Read_test2 プロジェクトでは、Post-Synthesis Fuctional Simulation はどうやっても動かないようだ。どこか回路がおかしいのかもしれない?

しかし、DMA Read IP は、論理シミュレーションはできるが、論理合成すると動作しないようだ。。。orz
休日の2日間ずっとやっていたが、どうにもならなかった。。。

Vivado HLS 2016.2 のバグなのか?それともVivado 2016.2 のバグなのか?わからないけれど?

そうだ。Vivado HLS 2015.4 でもやってみたが、同様だった。
  1. 2016年09月19日 07:22 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「レッドタートル ある島の物語」(映画)を見てきました

レッドタートル ある島の物語」(映画)を見てきました。セリフのない映画で、それなりによくできていたと思いますが、やはりセリフのある普通のアニメのほうが良かったです。私的には微妙でした。
  1. 2016年09月18日 20:02 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

DMA Read IP を単体でシミュレーション3(DMA Read IP単体で論理合成後にシミュレーション)

DMA Read IP を単体でシミュレーション2(論理合成後の機能シミュレーション)”の続き。

前回は、DMA Read IP 、AXI4 Slave BFM、 reg_write_read IP の3つをIP Integrator に入れて、論理合成をしていたので、AXI4 Slave BFM、 reg_write_read IP のどちらかが論理合成できない可能性もある。そこで、DMA Read IP だけをブロックデザインにAdd IP して、AXI4 Slave BFM、 reg_write_read IP は、シミュレーション・ファイルにすることにした。

DMA_Read_test2 プロジェクトを作成した。
DMA_Read_IP_test_23_160917.png

DMA_Read_test2 ブロックデザインを作成した。DMA Read IP と2つのAXI インターコネクトで構成されている。
DMA_Read_IP_test_25_160917.png

Address Editorの画面を示す。
DMA_Read_IP_test_26_160917.png

ブロックデザインのラッパーファイルを生成して、論理合成を行った。
その結果を示す。さすがにIOが465 になってしまってオーバーしてしまった。これでも論理合成後のシミュレーションには影響がないようだ。
DMA_Read_IP_test_24_160917.png

この状態で、Flow Navigator の Simulation -> Run Simulation をクリックして、Run Post-Synthesis Fuctional Simulation を行った。
DMA_Read_IP_test_20_160917.png

シミュレーション波形を示す。(2016/09/19 変更:やはり、Post-Synthesis Fuctional Simulation は動いていないようでした。画像を変更します)
DMA_Read_IP_test_37_160918.png 

DMA_Read_IP_test_38_160918.png 

問題なく動作しているようだ。つまりDMA Read IP は問題ないようだ。
すると、AXI4-Stream 版のビットマップ・ディスプレイ・コントローラがおかしいのか?今度はそれをシミュレーションしてみよう。
どうやっても、Post-Synthesis Fuctional Simulation でDMA Read IP が動かない。

最後にテストベンチの DMA_Read_test2_tb.v を貼っておく。

// DMA_Read_test2_tb.v
// 2016/09/15 by marsee
//

module DMA_Read_test2_tb;
    wire [31:0]M00_AXI_araddr;
    wire [1:0]M00_AXI_arburst;
    wire [3:0]M00_AXI_arcache;
    wire [7:0]M00_AXI_arlen;
    wire [1:0]M00_AXI_arlock;
    wire [2:0]M00_AXI_arprot;
    wire [3:0]M00_AXI_arqos;
    wire M00_AXI_arready;
    wire [3:0]M00_AXI_arregion;
    wire [2:0]M00_AXI_arsize;
    wire M00_AXI_arvalid;
    wire [31:0]M00_AXI_awaddr;
    wire [1:0]M00_AXI_awburst;
    wire [3:0]M00_AXI_awcache;
    wire [7:0]M00_AXI_awlen;
    wire [1:0]M00_AXI_awlock;
    wire [2:0]M00_AXI_awprot;
    wire [3:0]M00_AXI_awqos;
    wire M00_AXI_awready;
    wire [3:0]M00_AXI_awregion;
    wire [2:0]M00_AXI_awsize;
    wire M00_AXI_awvalid;
    wire M00_AXI_bready;
    wire [1:0]M00_AXI_bresp;
    wire M00_AXI_bvalid;
    wire [31:0]M00_AXI_rdata;
    wire M00_AXI_rlast;
    wire M00_AXI_rready;
    wire [1:0]M00_AXI_rresp;
    wire M00_AXI_rvalid;
    wire [31:0]M00_AXI_wdata;
    wire M00_AXI_wlast;
    wire M00_AXI_wready;
    wire [3:0]M00_AXI_wstrb;
    wire M00_AXI_wvalid;
    wire [31:0]S00_AXI_araddr;
    wire [1:0]S00_AXI_arburst;
    wire [3:0]S00_AXI_arcache;
    wire [7:0]S00_AXI_arlen;
    wire [0:0]S00_AXI_arlock;
    wire [2:0]S00_AXI_arprot;
    wire [3:0]S00_AXI_arqos;
    wire S00_AXI_arready;
    wire [3:0]S00_AXI_arregion;
    wire [2:0]S00_AXI_arsize;
    wire S00_AXI_arvalid;
    wire [31:0]S00_AXI_awaddr;
    wire [1:0]S00_AXI_awburst;
    wire [3:0]S00_AXI_awcache;
    wire [7:0]S00_AXI_awlen;
    wire [0:0]S00_AXI_awlock;
    wire [2:0]S00_AXI_awprot;
    wire [3:0]S00_AXI_awqos;
    wire S00_AXI_awready;
    wire [3:0]S00_AXI_awregion;
    wire [2:0]S00_AXI_awsize;
    wire S00_AXI_awvalid;
    wire S00_AXI_bready;
    wire [1:0]S00_AXI_bresp;
    wire S00_AXI_bvalid;
    wire [31:0]S00_AXI_rdata;
    wire S00_AXI_rlast;
    wire S00_AXI_rready;
    wire [1:0]S00_AXI_rresp;
    wire S00_AXI_rvalid;
    wire [31:0]S00_AXI_wdata;
    wire S00_AXI_wlast;
    wire S00_AXI_wready;
    wire [3:0]S00_AXI_wstrb;
    wire S00_AXI_wvalid;
    wire [1:0]active_frame_V;
    wire ap_clk;
    wire ap_rst_n;
    wire interrupt;
    wire [31:0]outs_tdata;
    wire [0:0]outs_tdest;
    wire [0:0]outs_tid;
    wire [3:0]outs_tkeep;
    wire [0:0]outs_tlast;
    wire outs_tready;
    wire [3:0]outs_tstrb;
    wire [0:0]outs_tuser;
    wire outs_tvalid;

    DMA_Read_test2_wrapper DMA_Read_test2_wrapper_i(
        .M00_AXI_araddr(M00_AXI_araddr),
        .M00_AXI_arburst(M00_AXI_arburst),
        .M00_AXI_arcache(M00_AXI_arcache),
        .M00_AXI_arlen(M00_AXI_arlen),
        .M00_AXI_arlock(M00_AXI_arlock),
        .M00_AXI_arprot(M00_AXI_arprot),
        .M00_AXI_arqos(M00_AXI_arqos),
        .M00_AXI_arready(M00_AXI_arready),
        .M00_AXI_arregion(M00_AXI_arregion),
        .M00_AXI_arsize(M00_AXI_arsize),
        .M00_AXI_arvalid(M00_AXI_arvalid),
        .M00_AXI_awaddr(M00_AXI_awaddr),
        .M00_AXI_awburst(M00_AXI_awburst),
        .M00_AXI_awcache(M00_AXI_awcache),
        .M00_AXI_awlen(M00_AXI_awlen),
        .M00_AXI_awlock(M00_AXI_awlock),
        .M00_AXI_awprot(M00_AXI_awprot),
        .M00_AXI_awqos(M00_AXI_awqos),
        .M00_AXI_awready(M00_AXI_awready),
        .M00_AXI_awregion(M00_AXI_awregion),
        .M00_AXI_awsize(M00_AXI_awsize),
        .M00_AXI_awvalid(M00_AXI_awvalid),
        .M00_AXI_bready(M00_AXI_bready),
        .M00_AXI_bresp(M00_AXI_bresp),
        .M00_AXI_bvalid(M00_AXI_bvalid),
        .M00_AXI_rdata(M00_AXI_rdata),
        .M00_AXI_rlast(M00_AXI_rlast),
        .M00_AXI_rready(M00_AXI_rready),
        .M00_AXI_rresp(M00_AXI_rresp),
        .M00_AXI_rvalid(M00_AXI_rvalid),
        .M00_AXI_wdata(M00_AXI_wdata),
        .M00_AXI_wlast(M00_AXI_wlast),
        .M00_AXI_wready(M00_AXI_wready),
        .M00_AXI_wstrb(M00_AXI_wstrb),
        .M00_AXI_wvalid(M00_AXI_wvalid),
        .S00_AXI_araddr(S00_AXI_araddr),
        .S00_AXI_arburst(S00_AXI_arburst),
        .S00_AXI_arcache(S00_AXI_arcache),
        .S00_AXI_arlen(S00_AXI_arlen),
        .S00_AXI_arlock(S00_AXI_arlock),
        .S00_AXI_arprot(S00_AXI_arprot),
        .S00_AXI_arqos(S00_AXI_arqos),
        .S00_AXI_arready(S00_AXI_arready),
        .S00_AXI_arregion(S00_AXI_arregion),
        .S00_AXI_arsize(S00_AXI_arsize),
        .S00_AXI_arvalid(S00_AXI_arvalid),
        .S00_AXI_awaddr(S00_AXI_awaddr),
        .S00_AXI_awburst(S00_AXI_awburst),
        .S00_AXI_awcache(S00_AXI_awcache),
        .S00_AXI_awlen(S00_AXI_awlen),
        .S00_AXI_awlock(S00_AXI_awlock),
        .S00_AXI_awprot(S00_AXI_awprot),
        .S00_AXI_awqos(S00_AXI_awqos),
        .S00_AXI_awready(S00_AXI_awready),
        .S00_AXI_awregion(S00_AXI_awregion),
        .S00_AXI_awsize(S00_AXI_awsize),
        .S00_AXI_awvalid(S00_AXI_awvalid),
        .S00_AXI_bready(S00_AXI_bready),
        .S00_AXI_bresp(S00_AXI_bresp),
        .S00_AXI_bvalid(S00_AXI_bvalid),
        .S00_AXI_rdata(S00_AXI_rdata),
        .S00_AXI_rlast(S00_AXI_rlast),
        .S00_AXI_rready(S00_AXI_rready),
        .S00_AXI_rresp(S00_AXI_rresp),
        .S00_AXI_rvalid(S00_AXI_rvalid),
        .S00_AXI_wdata(S00_AXI_wdata),
        .S00_AXI_wlast(S00_AXI_wlast),
        .S00_AXI_wready(S00_AXI_wready),
        .S00_AXI_wstrb(S00_AXI_wstrb),
        .S00_AXI_wvalid(S00_AXI_wvalid),
        .active_frame_V(active_frame_V),
        .ap_clk(ap_clk),
        .ap_rst_n(ap_rst_n),
        .interrupt(interrupt),
        .outs_tdata(outs_tdata),
        .outs_tdest(outs_tdest),
        .outs_tid(outs_tid),
        .outs_tkeep(outs_tkeep),
        .outs_tlast(outs_tlast),
        .outs_tready(outs_tready),
        .outs_tstrb(outs_tstrb),
        .outs_tuser(outs_tuser),
        .outs_tvalid(outs_tvalid)
    );
    assign outs_tready = 1'b1;
    
    reg_write_read reg_write_read_i (
        .ap_clk(ap_clk),
        .ap_rst_n(ap_rst_n),
        .ap_start(1'b1),
        .ap_done(),
        .ap_idle(),
        .ap_ready(),
        .m_axi_axi4m_AWVALID(S00_AXI_awvalid),
        .m_axi_axi4m_AWREADY(S00_AXI_awready),
        .m_axi_axi4m_AWADDR(S00_AXI_awaddr),
        .m_axi_axi4m_AWID(),
        .m_axi_axi4m_AWLEN(S00_AXI_awlen),
        .m_axi_axi4m_AWSIZE(S00_AXI_awsize),
        .m_axi_axi4m_AWBURST(S00_AXI_awburst),
        .m_axi_axi4m_AWLOCK(S00_AXI_awlock),
        .m_axi_axi4m_AWCACHE(S00_AXI_awcache),
        .m_axi_axi4m_AWPROT(S00_AXI_awprot),
        .m_axi_axi4m_AWQOS(S00_AXI_awqos),
        .m_axi_axi4m_AWREGION(S00_AXI_awregion),
        .m_axi_axi4m_AWUSER(),
        .m_axi_axi4m_WVALID(S00_AXI_wvalid),
        .m_axi_axi4m_WREADY(S00_AXI_wready),
        .m_axi_axi4m_WDATA(S00_AXI_wdata),
        .m_axi_axi4m_WSTRB(S00_AXI_wstrb),
        .m_axi_axi4m_WLAST(S00_AXI_wlast),
        .m_axi_axi4m_WID(),
        .m_axi_axi4m_WUSER(),
        .m_axi_axi4m_ARVALID(S00_AXI_arvalid),
        .m_axi_axi4m_ARREADY(S00_AXI_arready),
        .m_axi_axi4m_ARADDR(S00_AXI_araddr),
        .m_axi_axi4m_ARID(),
        .m_axi_axi4m_ARLEN(S00_AXI_arlen),
        .m_axi_axi4m_ARSIZE(S00_AXI_arsize),
        .m_axi_axi4m_ARBURST(S00_AXI_arburst),
        .m_axi_axi4m_ARLOCK(S00_AXI_arlock),
        .m_axi_axi4m_ARCACHE(S00_AXI_arcache),
        .m_axi_axi4m_ARPROT(S00_AXI_arprot),
        .m_axi_axi4m_ARQOS(S00_AXI_arqos),
        .m_axi_axi4m_ARREGION(S00_AXI_arregion),
        .m_axi_axi4m_ARUSER(),
        .m_axi_axi4m_RVALID(S00_AXI_rvalid),
        .m_axi_axi4m_RREADY(S00_AXI_rready),
        .m_axi_axi4m_RDATA(S00_AXI_rdata),
        .m_axi_axi4m_RLAST(S00_AXI_rlast),
        .m_axi_axi4m_RID(),
        .m_axi_axi4m_RUSER(),
        .m_axi_axi4m_RRESP(S00_AXI_rresp),
        .m_axi_axi4m_BVALID(S00_AXI_bvalid),
        .m_axi_axi4m_BREADY(S00_AXI_bready),
        .m_axi_axi4m_BRESP(S00_AXI_bresp),
        .m_axi_axi4m_BID(1'b0),
        .m_axi_axi4m_BUSER(1'b0),
        .dummy_out(),
        .ap_return()
    );

    axi_slave_bfm #(
        .READ_DATA_IS_INCREMENT(1'b1)
    ) axi_slave_bfm_i (
        .ACLK(ap_clk),
        .ARESETN(ap_rst_n),
        .S_AXI_AWID(1'b0),
        .S_AXI_AWADDR(M00_AXI_awaddr),
        .S_AXI_AWLEN(M00_AXI_awlen),
        .S_AXI_AWSIZE(M00_AXI_awsize),
        .S_AXI_AWBURST(M00_AXI_awburst),
        .S_AXI_AWLOCK(M00_AXI_awlock),
        .S_AXI_AWCACHE(M00_AXI_awcache),
        .S_AXI_AWPROT(M00_AXI_awprot),
        .S_AXI_AWQOS(M00_AXI_awqos),
        .S_AXI_AWUSER(1'b0),
        .S_AXI_AWVALID(M00_AXI_awvalid),
        .S_AXI_AWREADY(M00_AXI_awready),
        .S_AXI_WDATA(M00_AXI_wdata),
        .S_AXI_WSTRB(M00_AXI_wstrb),
        .S_AXI_WLAST(M00_AXI_wlast),
        .S_AXI_WUSER(1'b0),
        .S_AXI_WVALID(M00_AXI_wvalid),
        .S_AXI_WREADY(M00_AXI_wready),
        .S_AXI_BID(),
        .S_AXI_BRESP(M00_AXI_bresp),
        .S_AXI_BUSER(),
        .S_AXI_BREADY(M00_AXI_bready),
        .S_AXI_ARID(1'b0),
        .S_AXI_ARADDR(M00_AXI_araddr),
        .S_AXI_ARLEN(M00_AXI_arlen),
        .S_AXI_ARSIZE(M00_AXI_arsize),
        .S_AXI_ARBURST(M00_AXI_arburst),
        .S_AXI_ARLOCK(M00_AXI_arlock),
        .S_AXI_ARCACHE(M00_AXI_arcache),
        .S_AXI_ARPROT(M00_AXI_arprot),
        .S_AXI_ARQOS(M00_AXI_arqos),
        .S_AXI_ARUSER(1'b0),
        .S_AXI_ARVALID(M00_AXI_arvalid),
        .S_AXI_ARREADY(M00_AXI_arready),
        .S_AXI_RID(),
        .S_AXI_RDATA(M00_AXI_rdata),
        .S_AXI_RRESP(M00_AXI_rresp),
        .S_AXI_RLAST(M00_AXI_rlast),
        .S_AXI_RUSER(),
        .S_AXI_RVALID(M00_AXI_rvalid),
        .S_AXI_RREADY(M00_AXI_rready)
    );

    // sys_clock
    clk_gen #(
        .CLK_PERIOD(100),    // 10.0nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) sys_clock_i (
        .clk_out(ap_clk)
    );
    
    // reset_rtl_n
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESET2i (
        .reset_out(ap_rst_n)
    );
    
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule    

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

DMA Read IP を単体でシミュレーション2(論理合成後の機能シミュレーション)

DMA Read IP を単体でシミュレーション1(論理シミュレーション)”の続き。

前回はDMA Read IP を論理シミュレーションしたが、動作に問題はなかった。今回は、論理合成を行った後に論理合成後のシミュレーションを行った。

AXI4 Slave BFM も reg_write_read IP も論理合成可能なので、この2つ共々、DMA Read IP を論理合成した。その結果を示す。
DMA_Read_IP_test_17_160915.png

Flow Navigator の Simulation -> Run Simulation をクリックして、Run Post-Synthesis Fuctional Simulation を選択した。
DMA_Read_IP_test_18_160915.png

Post-Synthesis Fuctional Simulation 波形を示す。
DMA_Read_IP_test_16_160915.png

見事に何も波形が出力されていない。

試しに、PWMmodule_test プロジェクトを論理合成して、Post-Synthesis Fuctional Simulation をしてみた。
結果のシミュレーション波形を示す。
DMA_Read_IP_test_19_160915.png

きちんと波形が出ている。、Post-Synthesis Fuctional Simulation がうまくいかないということではないようだ。

DMA Read IP のみのプロジェクトにしてみて、、Post-Synthesis Fuctional Simulation を行うとどうなるだろうか?
  1. 2016年09月15日 04:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Zybotのカメラによる白線追従走行

Zybot の白線検出、追従走行のアプリケーション・ソフトウェアを作って、白線追従走行させてみました。
その様子です。




まだまだ、ON-OFF 制御で走行もふらつき気味ですが、一応、白線追従走行はできるようになりました。
ただ、直角に曲がるのは難しいですね。後ろの車輪をサーボモーターで向きを変えるか?前輪を左右逆転するとかしないといけないのでしょうか?
もっと車輪の間隔を狭めた方が小回り効きそうです。トレッドを短くするということですね。
ロボットカーは画像処理の要素だけでなく、車の要素が入ってくるので、制御が難しいですね。。。
なお、現在のZybot は、後車輪の自在キャスターが回りすぎてしまうので、クリップを改造して回り止めを付けています。
Zybot_160914.jpg

現在の制御用のアプリケーション・ソフトウェアを貼っておきます。

// wl_tracking.cpp
// 2016/09/02 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 "xpwm.h"
#include "xmotor_monitor.h"

#define DIR_LEFT_NORMAL        1
#define DIR_LEFT_REVERSE    0
#define DIR_RIGHT_NORMAL    0
#define DIR_RIGHT_REVERSE    1

#define PIXEL_NUM_OF_BYTES    4
#define SVGA_HORIZONTAL_PIXELS  800
#define SVGA_VERTICAL_LINES     600
#define SVGA_ALL_DISP_ADDRESS   (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)

#define GABOR_DETECT_LINE        590
#define GABOR_DETECT_LINE_ADDR    (SVGA_HORIZONTAL_PIXELS * GABOR_DETECT_LINE * PIXEL_NUM_OF_BYTES)
#define GABOR_THRESHOLD            5
#define DIST_THRESHOLD            30

#define LEFT_GABOR_EDGE_OVERFLOW    0
#define RIGHT_GABOR_EDGE_OVERFLOW    (SVGA_HORIZONTAL_PIXELS/2)

#define DEBUG
//#define MOTOR_OFF
#define MEMCPY

// Gobor filter
//
volatile unsigned *gabor_fil_on(int &fd4){
    int fd2, fd3;
    volatile unsigned *axis_switch_0, *axis_switch_1;
    volatile unsigned *gabor_filter_lh_0;
    int gabor_cntrl;

   // axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open error\n");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!axis_switch_0){
        fprintf(stderr, "axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open error\n");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!axis_switch_1){
        fprintf(stderr, "axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // gabor_filter_lh_0 (UIo14)
    fd4 = open("/dev/uio14", O_RDWR); // gabor_filter_lh_0 interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio14 (gabor_filter_lh_0) open error\n");
        exit(-1);
    }
    gabor_filter_lh_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!gabor_filter_lh_0){
        fprintf(stderr, "lap_filter_axis_0 mmap error\n");
        exit(-1);
    }
      
    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000; disable
    axis_switch_1[18] = 0// 0x48 = 0;
    axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
    // gabor filter AXIS Start
    gabor_filter_lh_0[6] = 0// left parameter
    gabor_cntrl = gabor_filter_lh_0[0] & 0x80// Auto Restart bit
    gabor_filter_lh_0[0] = gabor_cntrl | 0x01// Start bit set
    gabor_filter_lh_0[0] = 0x80// Auto Restart bit set
    
    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x2// 0x40 = 0x2;
    axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers

    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    
    close(fd2);
    close(fd3);
    
    return(gabor_filter_lh_0);
}

int search_gabor_edge(unsigned int start_addr, unsigned int number, int threshold){
    volatile int *imgaddr, *endaddr;
    int i;
    
    imgaddr = (volatile int *)start_addr;
    
    for (i=0; i<number; i++){
        int c=imgaddr[i] & 0xff;
        //printf("%d\n",c);
        if (c >= threshold){
            break;
        }
    }
    return(i); 
}

// Motor
//
void motor_settings(XPwm *motorLp, XPwm *motorRp){
    XPwm_DisableAutoRestart(motorLp);
    while(!XPwm_IsIdle(motorLp)) ;
    XPwm_Start(motorLp);
    XPwm_EnableAutoRestart(motorLp);
    
     XPwm_DisableAutoRestart(motorRp);
    while(!XPwm_IsIdle(motorRp)) ;
    XPwm_Start(motorRp);
    XPwm_EnableAutoRestart(motorRp);
}

void Stopped_Zybot(XPwm *motorLp, XPwm *motorRp){
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_sw_late_V(motorRp, 0);
}

void motor_initialize(XPwm &motorL, XPwm &motorR, XMotor_monitor &mmL, XMotor_monitor &mmR){
    XPwm *motorLp, *motorRp;
    XMotor_monitor *mmLp, *mmRp;
    
    motorLp = &motorL;
    motorRp = &motorR;
    mmLp = &mmL;
    mmRp = &mmR;
    
    // Initialization of motor
    if (XPwm_Initialize(motorLp, "pwm_0") != XST_SUCCESS){
        fprintf(stderr,"pwm_0 (Left) open error\n");
        exit(-1);
    }
    if (XPwm_Initialize(motorRp, "pwm_1") != XST_SUCCESS){
        fprintf(stderr,"pwm_1 (Right) open error\n");
        exit(-1);
    }
    
    
    // Initialization of motor monitor
    if (XMotor_monitor_Initialize(mmLp, "motor_monitor_0") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_0 (Left) open error\n");
        exit(-1);
    }
    if (XMotor_monitor_Initialize(mmRp, "motor_monitor_1") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_1 (Right) open error\n");
        exit(-1);
    }

    // The Motors is rotated in the forward direction.
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_dir_V(motorLp, 1);

    XPwm_Set_sw_late_V(motorRp, 0);
     XPwm_Set_dir_V(motorRp, 0);

    motor_settings(motorLp, motorRp);
}


int main(){
    int fd4;
    volatile unsigned *gabor_filter_lh_0;
    XPwm motorL, motorR;
    XMotor_monitor mmL, mmR;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
      int left_wl_edge, right_wl_edge;

    // Gabor filter Initialize
    gabor_filter_lh_0 = gabor_fil_on(fd4);

    // Motor Initialize
    motor_initialize(motorL, motorR, mmL, mmR);
    
    // udmabuf0
    int fdf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The cache is disabled. 
    if (fdf == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    volatile unsigned *frame_buffer = (volatile unsigned *)mmap(NULL, 3*SVGA_ALL_DISP_ADDRESS, PROT_READ|PROT_WRITE, MAP_SHARED, fdf, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    int fdp = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fdp == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fdp, attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);  
    close(fdp);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);
    
    // main loop
    printf("White line Tracking start. \n");
    while(1){
        // Gabor filter for left white line 
        gabor_filter_lh_0[6] = 0// left parameter

        usleep(250000); // Wait 67 ms
        left_wl_edge = SVGA_HORIZONTAL_PIXELS/2 - search_gabor_edge(
            (unsigned int)frame_buffer+GABOR_DETECT_LINE_ADDR, SVGA_HORIZONTAL_PIXELS/2, GABOR_THRESHOLD);
#ifdef MEMCPY
        memcpy((unsigned int *)((unsigned int)frame_buffer+SVGA_ALL_DISP_ADDRESS), (unsigned int *)frame_buffer,
            SVGA_ALL_DISP_ADDRESS);
#endif

        // Gabor filter for right white line
        gabor_filter_lh_0[6] = 1// right parameter
        usleep(250000); // Wait 67 ms
        right_wl_edge = search_gabor_edge(
            (unsigned int)frame_buffer+GABOR_DETECT_LINE_ADDR+(SVGA_HORIZONTAL_PIXELS/2)*PIXEL_NUM_OF_BYTES,
            SVGA_HORIZONTAL_PIXELS/2, GABOR_THRESHOLD);
#ifdef MEMCPY
        memcpy((unsigned int *)((unsigned int)frame_buffer+2*SVGA_ALL_DISP_ADDRESS), (unsigned int *)frame_buffer,
            SVGA_ALL_DISP_ADDRESS);
#endif

#ifdef DEBUG
        printf("left_wl_edge = %d, right_wl_edge = %d\n", left_wl_edge, right_wl_edge);
#endif

        if (left_wl_edge == LEFT_GABOR_EDGE_OVERFLOW){
#ifndef MOTOR_OFF
            XPwm_Set_sw_late_V(&motorL, 5);
            XPwm_Set_sw_late_V(&motorR, 35);
#endif
#ifdef DEBUG
            printf("Left gabor edge is overflow\n");
#endif
        } else if (right_wl_edge == RIGHT_GABOR_EDGE_OVERFLOW){
#ifndef MOTOR_OFF
            XPwm_Set_sw_late_V(&motorL, 35);
            XPwm_Set_sw_late_V(&motorR, 5);
#endif
#ifdef DEBUG
            printf("Right gabar edge is overflow\n");
#endif
        } else if ((right_wl_edge - left_wl_edge) > DIST_THRESHOLD){
#ifndef MOTOR_OFF
            XPwm_Set_sw_late_V(&motorL, 25);
            XPwm_Set_sw_late_V(&motorR, 15);
#endif
#ifdef DEBUG
            printf("Right turn\n");
#endif
        } else if ((right_wl_edge - left_wl_edge) < -DIST_THRESHOLD){
#ifndef MOTOR_OFF
            XPwm_Set_sw_late_V(&motorL, 15);
            XPwm_Set_sw_late_V(&motorR, 25);
#endif
#ifdef DEBUG
            printf("Left turn\n");
#endif
        } else if (abs(right_wl_edge - left_wl_edge) <= DIST_THRESHOLD){
#ifndef MOTOR_OFF
            XPwm_Set_sw_late_V(&motorL, 20);
            XPwm_Set_sw_late_V(&motorR, 20);
#endif
#ifdef DEBUG
            printf("Go straight\n");
#endif
        }
    }
}


なぜか、ガボール・フィルタを設定してから、67 ms 待つだけでは画像が完全に処理しきれていないことがあったので、待ち時間を延ばしました。なんででしょうか?
左右のガボール・フィルタ画像を別のフレームバッファに割り当ててDMA転送すれば、もっと速くできそうです。
カメラのfps を上げることも考えてみようと思います。
  1. 2016年09月14日 04:44 |
  2. 白線検出
  3. | トラックバック:0
  4. | コメント:0

DMA Read IP を単体でシミュレーション1(論理シミュレーション)

Vivado HLSで作ったDMA Read IP を実機でテスト1(動作しない)”で DMA Read IP が全く動作しないので、単体でシミュレーションしみることにした。

シミュレーションするにあたって、必要なIP は、”AXI4 Slave Bus Functional Model のVerilog HDL版4”と、”レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた”で作った reg_write_read IP だ。

DMA_Read_test プロジェクトを作成して、 reg_write_read IP をIP Catalog に登録した。AXI4 Slave Bus Functional Model のVerilog HDL版はプロジェクトに入れて使用しようと思ったのだが、面倒なので axi_slave_bfm IP を作ることにした。

axi_slave_bfm プロジェクトを作成して、Tools メニューから Create and Package IP... を選択してIP 化を行った。
DMA_Read_IP_test_3_160913.png

上の図で、edit packaging settings をクリックすると、Project Settings のIP が開くので、Create archive of IP にチェックと入れると、IP がまとめらえたZIP ファイルができる。
DMA_Read_IP_test_4_160913.png

それが、xilinx.com_user_axi_slave_bfm_1.0.zip だ。
DMA_Read_IP_test_5_160913.png

xilinx.com_user_axi_slave_bfm_1.0.zip の中身を示す。
DMA_Read_IP_test_6_160913.png

DMA_Read_test フォルダの下に axi_slave_bfm フォルダを作って、そのZIP ファイルの中身を入れた。

DMA_Read_test プロジェクトで axi_slave_bfm フォルダをIP Catalog に登録した。
DMA_Read_IP_test_7_160913.png

DMA_Read_test ブロックデザインを生成して、Add IP して回路を構成した。
DMA_Read_IP_test_8_160913.png

Address Editor。
DMA_Read_IP_test_9_160913.png

DMA Read IP のレジスタを設定する reg_write_read IP を編集して、レジスタに書き込む値をセットする。
DMA_Read_IP_test_10_160913.png

reg_write_read_reg_?_rom.dat を編集する。dat ファイルの機能の割り当てを示す。

reg_write_read_reg_0_rom.dat : Write - 0, Read - 1
reg_write_read_reg_1_rom.dat : Delay、単位はクロック数
reg_write_read_reg_2_rom.dat : アドレス、16進数
reg_write_read_reg_3_rom.dat : データ、16進数


実際にレジスタをセットする値を示す。

R/W  Delay Address      value
0    0    0x44A00018    0x10000000
0    0    0x44A00020    0x10000000
0    0    0x44A00028    0x10000000
0    0    0x44A00030    0x00000000
0    0    0x44A00000    0x00000001


実際に値を書き込んでいるところだ。
DMA_Read_IP_test_11_160913.png

これで Re-Package IP を行う。

次に、今回は、AXI4 Master Read のみなので、AXI4 Slave BFM の設定をRead されたら 0 から順に +1 された出力を出すモードに変更した。
DMA_Read_IP_test_12_160913.png

これで論理シミュレーションの準備が整ったので、シミュレーションを行った。
DMA_Read_IP_test_13_160913.png

シミュレーション波形を示す。動作している問題ないようだ。
最初に、AXI4-Stream 出力とレジスタ設定用のAXI4-Lite Slave Write を示す。AXI4-Stream 出力も出力されている。
DMA_Read_IP_test_14_160913.png

AXI4 Master Read を示す。こちらも正常のようだ。
DMA_Read_IP_test_15_160913.png

論理シミュレーションは正常のようだが、それじゃなんで動作しないのだろうか?
  1. 2016年09月13日 21:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったDMA Read IP を実機でテスト1(動作しない)

Vivado HLSで作ったDMA Write IP を実機でテスト4(成功)”に続いて、”Vivado HLS で DMA Read IP を作る(絶対アドレス指定版)”で作ったDMA Read IP をテストする。

Vivado HLSで作ったDMA Write IP を実機でテスト4(成功)”のプロジェクトをそのまま使用することにした。
今まで使用していた ZYBO_0_162_3_t0 フォルダを ZYBO_0_162_3_t1 フォルダにコピーして使用した。

VGA出力を担当しているビットマップ・ディスプレイ・コントローラを削除して、DMA Read IP と”ビットマップ・ディスプレイ・コントローラをAXI4-Stream対応にする5(IP作成)”で作成した AXI4-Stream 版ビットマップ・ディスプレイ・コントローラを追加した。
その際に、AXI4 Master 版のビットマップ・ディスプレイ・コントローラとAXI4-Stream 版のビットマップ・ディスプレイ・コントローラが2つプロジェクトに入ったので、bitmap_disp_engine.v の Verilog HDL のモジュール名が重なってエラーが出てしまった。異なるIP でも同じモジュール名があるとダメなようなので、AXI4-Stream 版のビットマップ・ディスプレイ・コントローラのAXI4-Stream 版ビットマップ・ディスプレイ・コントローラの bitmap_disp_engine.v の Verilog HDL のモジュール名を bitmap_disp_eng_axis に変更した。
ブロックデザインを示す。
DMA_Read_IP_test_1_160912.png

すでにVivado Analyzer を入れてある。

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

この回路を論理合成、インプリメント、ビットストリームの生成を行い、SDKにハードウェアをエクスポートして、ZYBOでやってみたがやはりだめだった。VGA出力が出ていなかった。

とりあえずはDMA Read IP にVivado Analyzer を入れて確かめてみたが、AXI4 Master アクセスが出ていなかった。
AXI4 Lite Slave による設定はきちんとされているようなので、動かないはずはないと思うのだが。。。
DMA Read IP を単体でシミュレーションしてみることにしようと思う。
  1. 2016年09月13日 04:11 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS でシミュレーション用と合成用の記述を書き分ける

今までは、Vivado HLS で、シミュレーションするときと合成するときの記述の切り替えは、コメントで切り分けていた。
シミュレーションするときに大きな画像などを対象とするとシミュレーション時間がかかってしまうので、できれば小さい画像を使いたい。合成時は実際に扱う画像の大きさを指定する必要があるということで、記述の切り替えを忘れてしまって痛い目を見ることがある。

(注)合成の時に本番用のパラメータを使ってしまうので、C/RTL 協調シミュレーションがうまく行かなくなってしまうという欠点があります。ご注意を。。。

今回は、tu1978 さんに教えて頂いたコードの中に合成の時だけ有効になるマクロがあるのでそれを使用する。そのマクロは”__SYNTHESIS__”だ。
これは、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.1) 2016 年 4 月 6 日”の 253 ページに書いてある。その説明を引用する。

Vivado HLS では、 合成が実行 された と きに macro __SYNTHESIS__ が定義されます。 これによ り 、 __SYNTHESIS__
macro を使用し て、 デザ イ ンか ら合成不可能な コー ド を削除でき る よ う にな り ます。


この __SYNTHESIS__ を使用して、”Vivado HLSで作ったDMA Write IP を実機でテスト4(成功)”でミスったしまったヘッダファイルDMA_Write.h を書き直してみよう。

直す前のDMA_Write.h はこのように書かれていた。
DMA_Write_IP_test_29_160908.png

これを、 __SYNTHESIS__ を使用して、下のように書き直した。

// DMA_Write.h
// 2016/07/10 by marsee
// 2016/09/09 __SYNTHESIS__を使うように修正
//

#ifndef __DMA_WRITE_H__
#define __DMA_WRITE_H__

#ifdef __SYNTHESIS__
    #define HORIZONTAL_PIXEL_WIDTH    800
    #define VERTICAL_PIXEL_WIDTH    600
#else
    #define HORIZONTAL_PIXEL_WIDTH    64
    #define VERTICAL_PIXEL_WIDTH    48
#endif

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    3

#endif


これでシミュレーション時の記述と合成時の記述を分けることができた。
  1. 2016年09月10日 05:36 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったDMA Write IP を実機でテスト4(成功)

Vivado HLSで作ったDMA Write IP を実機でテスト3(Vivado Analyzer)”の続き。

前回は、DMA Write IP がうまく動作しなかった原因は、画面サイズが 800 x 600 ピクセルなのに、シミュレーション用の画像サイズの 64 x 48 ピクセルに設定されていたことが原因だった。今回は、画面サイズを 800 x 600 ピクセルに修正して、IP を更新して、再度テストすることにした。

Vivado HLS 2016.2 を立ち上げて、DMA_Write.h を 800 x 600 サイズに変更した。
DMA_Write_IP_test_28_160908.png

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

IP 化を行って、Vivado のDMA Write IP を更新して、論理合成、インプリメント、ビットストリームの生成を行った。
ハードウェアをエクスポートして、SDKを立ち上げ、ZYBO にビットストリームをダウンロードし、再度アプリケーションを起動したら、画面が出ました。
DMA_Write_IP_test_30_160910.jpg

成功した。
あとは、DMA Read IP をテストしよう。
  1. 2016年09月10日 05:21 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったDMA Write IP を実機でテスト3(Vivado Analyzer)

Vivado HLSで作ったDMA Write IP を実機でテスト2(実機テスト)”の続き。

前回はZYBO_0_2 プロジェクトにDMA Write IP を入れたプロジェクトでカメラの画像を表示できないという結果だった。今回は、Vivado Analyzer を使って、なぜDMA Write IP が動作しないか?を探った。その結果、原因は判明した。

まずは、camera_interface ブロックデザインでDMA_Write_0 IP の周りのバスをMark Debug した。
DMA_Write_IP_test_17_160908.png

これで、論理合成し、Open Synthesized Design を起動した。Tools メニューからSet up Debug を行って、Vivado Analyzer に入れるプローブを設定して、インプリメントを行ったが、タイミングエラーになってしまった。
DMA_Write_IP_test_18_160908.png

Open Implemented Design を開いて、Timing Summary を見たところ、Intra-Clock の clk_fpga_0 がタイミングエラーを起こしていた。
DMA_Write_IP_test_19_160908.png

そこで、ブロックデザインで ZYNQ をダブルクリックして開き、FCLK_CLK0 を 100 MHz から 90 MHz に変更した。
DMA_Write_IP_test_20_160908.png

これで、論理合成、インプリメントを行うと、今度は、Inter-Clock Paths でエラーになった。
DMA_Write_IP_test_21_160908.png

clk_fpga_0, clk_fpga_1 間のタイミングを無視する設定を制約ファイルに加えた。
DMA_Write_IP_test_22_160908.png

これで、論理合成、インプリメント、ビットストリームの生成を行うと、タイミングエラーは無くなった。
DMA_Write_IP_test_23_160908.png

Vivado Analyzer の結果を示す。これは、DMA Write IP のAXI4-Lite Slave の最初のレジスタの設定タイミングが表示されている。
3つのフレームバッファのベースアドレス・レジスタに 0x10000000 のアドレスを書き込んでいる。
DMA_Write_IP_test_24_160908.png

次の波形は、ap_start に 1 を書き込んで、次は、auto_restart を 1 に設定している。
DMA_Write_IP_test_25_160908.png

AXI4-Stream もきちんと来ているし、AXI4 Master も出ている。ちなみにAWADDR は下2ビットがないので、0x04000000 のアドレスとなっている。
DMA_Write_IP_test_26_160908.png

Vivado Analyzer をフリートリガにしてみると、AWADDR が 0x04000bf0 で止まっているようだ。
DMA_Write_IP_test_27_160908.png

あ~。これは、シミュレーションで 'A' という画像のシミュレーションを行っているときの数だ。画像のサイズを 800 x 600 ピクセルにしていなかったようだ。
  1. 2016年09月09日 04:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったDMA Write IP を実機でテスト2(実機テスト)

Vivado HLSで作ったDMA Write IP を実機でテスト1(インプリメント)”の続き。

前回は、Vivado HLS で作った DMA Write IPをZYBO_0_2 プロジェクトに入れて、論理合成、インプリメント、ビットストリームの生成を行った。今回は、ハードウェアをエクスポートして、SDKでアプリケーションを作成し、ZYBO 実機でテストしてみた。

ハードウェアをエクスポートして、SDKを立ち上げ、アプリケーションを作成した。
DMA_Write_IP_test_15_160908.png

ZYBOにビットストリームをダウンロードして、アプリケーションを起動すると、砂嵐パターンが表示された。
DMA_Write_IP_test_16_160908.jpg

たぶんカメラのデータをフレームバッファに書き込むDMA Write が動作していないのだろう。つまり、 DMA Write IP がおかしいようだ。
次は、Vivado Analyzer を入れて様子を見てみようと思う。

最後に、作成したアプリケーションの cam_diap3_axis.c を貼っておく。

// cam_disp3_axis.c
// 2016/09/03 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 "xdma_write.h"
#include "sleep.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(){
    XDma_write dmaw;

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

    // Initialization of DMA Write
    if (XDma_write_Initialize(&dmaw, 0) != XST_SUCCESS){
        fprintf(stderr, "DMA Write IP open error");
        exit(-1);
    }

    XDma_write_Set_frame_buffer0(&dmaw, FRAME_BUFFER_ADDRESS);
    XDma_write_Set_frame_buffer1(&dmaw, FRAME_BUFFER_ADDRESS);
    XDma_write_Set_frame_buffer2(&dmaw, FRAME_BUFFER_ADDRESS);

    while(!XDma_write_IsIdle(&dmaw));
    XDma_write_Start(&dmaw);
    XDma_write_EnableAutoRestart(&dmaw);

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

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

リコンフ研に行ってきました

9月5日(月)、6日(火)とリコンフィギャラブルシステム研究会(RECONF)を聴講するために富山に行ってきました。

初めての富山で、路面電車にも久しぶりに乗りました。路面電車に乗るのは初めてかと思ったのですが、遠い昔に長崎観光したときに乗ったと思います。
お寿司も食べてきましたが、美味しかったのですが、値段が高かったです。
富山大学の学食はお米が美味しかったですね。

リコンフ研は、FPGA についての発表だらけで、楽しかったです。宴会も盛り上がったし、また行きたいですね。
お世話になった先生方、ツィッターでお世話になっている先生方、ありがとうございました。
また、行こうと思っているので、よろしくお願い致します。
  1. 2016年09月07日 04:41 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

FPGAコンパイルマシンが届きました(新しいパソコン)

新しいパソコンが土曜日の9月3日に届きました。
new_pc_1_160905.jpg

早速、箱から出して床置きです。今までパソコンはブログ書きやインターネットを見るのに使用して、新しいのはFPGAコンパイルマシンとして使う予定です。
new_pc_2_160905.jpg

日曜日には、Vivado や SDSoC をインストールしました。段々と環境を構築していきます。

なお、今日、明日(9月5日、6日)とリコンフィギャラブルシステム研究会(RECONF)の聴講に富山大学に行ってきます。いろいろな話が聞けるのではないか?と楽しみにしております。懇親会も楽しみですね。ツィッターでお世話になっている先生方とお話ができるんが楽しみです。
  1. 2016年09月05日 04:08 |
  2. パソコン関連
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったDMA Write IP を実機でテスト1(インプリメント)

Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム2(SDK)”で、Vivado HLS で作った DMA Write IP と DMA Read IP 、そして新たに作ったAXI4-Stream版ビットマップ・ディスプレイ・コントローラをテストしたが、動作しなった。更に、Vivado Analyzer でデバックしようとしたが、一部動作したり、しなかったりで、どうにもうまくデバックができなかった。

そこで、”Zybot のカメラ画像でGabor Filterのパラメータを取得した3(Zybotでテスト)”で実際に使用しているプロジェクトで、AXI VDMA IPと”Vivado HLS で DMA Write IP を作る(絶対アドレス指定編)”のDMA Write IP を入れ替えてテストしてみようと思う。

まずはプロジェクトを示す。Z:/test/ZYBO_0_162_3_t0 フォルダに入れてある。
DMA_Write_IP_test_1_160904.png

camera_interface モジュールのAXI VDMA を DMA_Write IP に変更した。
DMA_Write_IP_test_2_160904.png

これで、論理合成、インプリメントすると、タイミングエラーが発生した。
DMA_Write_IP_test_3_160904.png

Open Implemented Design で調べてみると、ガボール・フィルタの内部でエラーになっていた。動作周波数は 100 MHz。
DMA_Write_IP_test_4_160904.png

ガボール・フィルタはVivado HLS で作ってあるので、Vivado HLS のGabor_filter_lh プロジェクトを開いて、Target は今まで 7 ns だったところを 6 ns に変更して、C コードの合成を行った。
DMA_Write_IP_test_5_160904.png

C コードの合成のレポートの続き。
DMA_Write_IP_test_6_160904.png

これで IP 化を行って、この IP をVivado プロジェクトのガボール・フィルタIP と入れ替えて、もう一度、論理合成、インプリメント、ビットストリームの生成を行った。
やはり、タイミングエラー。しかも悪くなっている。
DMA_Write_IP_test_7_160904.png

やはり、ガボール・フィルタの内部でタイミングエラーが発生している。ロジック遅延も少し増えているが、ネット遅延が8 ns 程度から 11 ns程度に増えている。
DMA_Write_IP_test_8_160904.png

これは案外、Vivado HLS のTarget 時間を延ばした方が良いのか?ということで、Target を 8 ns にしてみた。
DMA_Write_IP_test_9_160904.png DMA_Write_IP_test_10_160904.png

これで、C コードの合成、IP 化した。

Vivado のガボール・フィルタのIP を更新して、もう一度、論理合成、インプリメント、ビットストリームの生成を行った。
DMA_Write_IP_test_11_160904.png

やはり、タイミングエラーが発生している。

仕方がないのでガボール・フィルタを削除した。
DMA_Write_IP_test_12_160904.png

これで、再々度、論理合成、インプリメント、ビットストリームの生成を行った。
DMA_Write_IP_test_13_160904.png

今度もほんの少しではあるが、タイミングエラーが発生している。

詳しく見てみると、ラプラシアンフィルタでの1個だけなので、これで良しとした。
DMA_Write_IP_test_14_160904.png
  1. 2016年09月04日 09:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Zybotの最初の白線検出、走行プログラムの覚書

Zybot の最初の白線検出、走行プログラムの覚書を書いておく。
Zybot が白線の間を走るアプリケーション・ソフトウェアを作るのだが、どのように作れば良いか?最初に具合を検証するために作る。とりあえず、モーターの回転センサは未使用とする。このソフトウェアを作ってどのように走るか見てみよう。

最初の白線検出、走行プログラム

./cam_disp_vdma でカメラ画像を表示する。


    ガボール・フィルタを表示する。

    左白線表示にして、67ms(15fps) Waitする。

    左白線位置を検出する。(左白線用ガボールフィルタの画像の下 10ライン、つまり590ライン目画面の左端から半分まで走査する)
    
    右白線表示にして、67ms(15fps) Waitする。

    右白線位置を検出する。(右白線用ガボールフィルタの画像の下 10ライン、つまり590ライン目画面の半分から右端まで走査する)

    左白線位置(左白線エッジから画像中央)と右白線位置(画像中央から右白線エッジ)を比較する。
        
        |右白線位置-左白線位置| <= 10   : 左モーターPWM = 30%, 右モーターPWM = 30%

        右白線位置が未検出 :                 左モーターPWM = 40%, 右モーターPWM = 30%

        左白線位置が未検出 :                  左モーターPWM = 30%, 右モーターPWM = 40%

        右白線位置 - 左白線位置 > +10  : 左モーターPWM = 35%, 右モーターPWM = 30%

        右白線位置 - 左白線位置 < -10  : 左モーターPWM = 30%, 右モーターPWM = 35%

ガボール・フィルタのベースのRGB 値はどれも 0 なので、白線のエッジのスレッショルドはどれか1つの色が 50 になった時とする。

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

Zybot による白線間の自動走行3(実機テスト)

Zybot による白線間の自動走行2(Gabor fillter の修正、Vivado HLS)”の続き。

前回は、左白線検出、右白線検出を画像フレームごとに交互に繰り返すガボール・フィルタをIP 化した。今回は、それをVivado 2016.2 のIP として、回路に加えた。
具体的には、今回のガボール・フィルタIP をZYBO_0_162_3 フォルダのVivado プロジェクトの既存のガボール・フィルタIP と交換して論理合成、インプリメント、ビットストリームの生成を行った。ZYBO_0_162_4 フォルダとした。(”Gabor FilterをZYBO_0_162_2 へ実装してみた”を参照のこと)
これをビットストリームまで生成して、SDKでBOOT.bin を作成して、SDカードに書き込んでZYBO でブートした。
AXI VDMA のフレームバッファを 4 にして、ガボール・フィルタの左白線検出、右白線検出交互検出にしてみたが、左白線検出のフレームだけを表示しても、交互に表示されてしまう。

次に、ガボール・フィルタを 3 回フレームを処理するように書き換えて、2回と同様にやってみたが、交互に表示されるときと、右だけになる場合がある。

この方法でやる場合には、左白線検出、右白線検出の同期をとる必要があるようだ。

とりあえずは、最初の画像 1 フレームだけのガボール・フィルタに戻して、ソフトウェアで白線検出を試みることにしようと思う。
  1. 2016年09月01日 11:07 |
  2. 白線検出
  3. | トラックバック:0
  4. | コメント:0

Zybot による白線間の自動走行2(Gabor fillter の修正、Vivado HLS)

Zybot による白線間の自動走行1(Gabor fillter の修正、C ソースコード)”の続き。

前回は左白線検出と右白線検出を交互にできるように修正したガボール・フィルタのソースコードを貼った。今回は、Vivado HLS 2016.2 での結果を書いておく。

まずは、Vivado HLS 2016.2 でのプロジェクトを示す。
GaborFilter2_1_160901.png

次に、C シミュレーションを行った。なお、画面サイズは640 x 480 に変更してある。

Compiling ../../../Gabor_filter_lh_2_tb.cpp in debug mode
Compiling ../../../Gabor_filter_lh_2.cpp in debug mode
Generating csim.exe

outs
ERROR HW and SW results mismatch i = 557, j = 73, HW = 00171717, SW = 00141414
ERROR HW and SW results mismatch i = 419, j = 88, HW = 00171717, SW = 00141414
ERROR HW and SW results mismatch i = 419, j = 92, HW = 00131313, SW = 00101010
ERROR HW and SW results mismatch i = 405, j = 100, HW = 003e3e3e, SW = 003b3b3b
ERROR HW and SW results mismatch i = 426, j = 106, HW = 00121212, SW = 000f0f0f
ERROR HW and SW results mismatch i = 486, j = 119, HW = 00171717, SW = 00141414
ERROR HW and SW results mismatch i = 235, j = 120, HW = 001b1b1b, SW = 00181818
ERROR HW and SW results mismatch i = 437, j = 120, HW = 00e1e1e1, SW = 00dedede
ERROR HW and SW results mismatch i = 441, j = 120, HW = 00141414, SW = 00111111
ERROR HW and SW results mismatch i = 235, j = 121, HW = 00141414, SW = 00111111
ERROR HW and SW results mismatch i = 425, j = 124, HW = 00a9a9a9, SW = 00a6a6a6
ERROR HW and SW results mismatch i = 425, j = 125, HW = 00e1e1e1, SW = 00dedede
ERROR HW and SW results mismatch i = 252, j = 132, HW = 004e4e4e, SW = 004b4b4b
ERROR HW and SW results mismatch i = 254, j = 133, HW = 00d9d9d9, SW = 00d6d6d6
ERROR HW and SW results mismatch i = 296, j = 145, HW = 00161616, SW = 00131313
ERROR HW and SW results mismatch i = 296, j = 146, HW = 00181818, SW = 00151515
ERROR HW and SW results mismatch i = 135, j = 156, HW = 00dcdcdc, SW = 00d9d9d9
ERROR HW and SW results mismatch i = 137, j = 156, HW = 003f3f3f, SW = 003c3c3c
ERROR HW and SW results mismatch i = 139, j = 157, HW = 00040404, SW = 00010101
ERROR HW and SW results mismatch i = 491, j = 163, HW = 00efefef, SW = 00ececec
ERROR HW and SW results mismatch i = 495, j = 163, HW = 00f3f3f3, SW = 00f0f0f0
Success HW and SW results match

WARNING: Hls::stream 'hls::stream >.2' contains leftover data, which may result in RTL simulation hanging.
WARNING: Hls::stream 'hls::stream >.1' contains leftover data, which may result in RTL simulation hanging.
INFO: [SIM 1] CSim done with 0 errors.


結構、固定小数点演算と浮動小数点演算の二乗誤差が 9 以上の項が出ているが、9 で収まっているようだ。

次に、C コードの合成を行った。
GaborFilter2_2_160901.png

まずは、Timing のTarget をなぜ 10 ns ではなく、7 ns にしているかだが、10 ns で C コードを合成して、IP 化を行って、Vivado でIP を回路に加えてインプリメントすると、このガボール・フィルタのIP 内でクリティカルパスが発生して、100 MHzで動作しないからだ。クリティカルパスの遅延は、16 ns 程度になってしまった。、Timing のTarget を 7 ns にすると、Vivado でも 100 MHz で動作できるようだ。これは、Vivado HLS のクリティカルパスの遅延計算が甘いのかもしれない?

次に、Detail のLoop 2 を見ると、Latency が 614410 となっていた。画像の 640 x 480 x 2 画面 = 614400 なので、10 クロック余計なだけであるので、1クロックで 1 ピクセル処理ができていると言える。

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

Latency は 614454 クロックで、ここでも、1クロックで 1 ピクセル処理ができていると言えると思う。

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

ins_TVALID, ins_TREADY, outs_TVALID, outs_TREADY もずっと 1 で、1クロックで 1 ピクセル処理ができている。

画像を 800 x 600 に戻して、もう一度、C コードの合成を行ってから、IP 化を行った。
  1. 2016年09月01日 04:14 |
  2. 白線検出
  3. | トラックバック:0
  4. | コメント:0