FC2カウンター FPGAの部屋 Vivado HLS

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

FPGAの部屋

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

Vivado HLS で DMA Read IP を作る3(オフセット・アドレス指定版)

Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”の続き。

ついでに作ったので書いておこう。
Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”は絶対アドレスを3つ指定して、DMA Read を行うが、DMA Read IP のオフセット・アドレス指定版はベース・アドレスを指定し、ベースアドレスに対してのオフセット・アドレスを3つ指定してDMA Readするアドレスを決定する。ただし、C のソースコードはm_axi 指示子の depth と offset オプションのみが違っている。
depth は本来の値に設定する。そして、offset=slave とする必要がある。offset=slave とすると、ベース・アドレスがAXI4-Lite のレジスタとして実装される。
なおターゲットとしてはZYBO ボードとする。

Vivado HLS 2016.4 の DMA_Read_offset プロジェクトを示す。
DMA_Read_offset_1_170418.png

C シミュレーションを行った。成功した。
DMA_Read_offset_2_170418.png

C コードの合成を行った。結果を示す。
DMA_Read_offset_3_170418.png
ベース・アドレスのレジスタが増えているだけに、オフセット・アドレス版のほうがリソース使用量が多い。
アドレス・マップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of in_r
//        bit 31~0 - in_r[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of fb0_offset_addr
//        bit 31~0 - fb0_offset_addr[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of fb1_offset_addr
//        bit 31~0 - fb1_offset_addr[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of fb2_offset_addr
//        bit 31~0 - fb2_offset_addr[31:0] (Read/Write)
// 0x34 : reserved
// 0x38 : Data signal of mode_V
//        bit 0  - mode_V[0] (Read/Write)
//        others - reserved
// 0x3c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


DMAのベース・アドレスのレジスタ”0x18 : Data signal of in_r”があるのが分かる。

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

C/RTL協調シミュレーション波形を示す。3つのDMA Readが見えている。
DMA_Read_offset_5_170418.png
絶対アドレス版と違って、オフセット・アドレス版は dma_result0.bmp、dma_result1.bmp、dma_result2.bmp が正常に”A”になっている。

DMA_Read_offset_6_170418.png

Export RTL を行った。
DMA_Read_offset_7_170418.png

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


DMA_Read_offset.cpp を示す。

// DMA_Read_offset.cpp
// 2017/04/17 by marsee
//
// fb0_offset_addr, fb1_offset_addr, fb2_offset_addr には3つのフレームバッファのオフセット・アドレスを入れる
// mode = 0 : DMA Write IP の active_frame を見て、その1つ前のフレームをDMA Readするモード(DMA_WRITE_MODE)
// mode = 1 : フリーラン モード(FREE_RUN_MODE)
//
// カメラのフレームレートの方が小さい場合に、フリーランモードで3つフレームバッファが別々だと画像がぶれてしまう。
// その場合は、3つのフレームバッファ・アドレスを1つのアドレスを入れて、シングル・フレームバッファとする必要がある。

// 1フレーム分のDMAを行う
//

#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_offset(volatile int *in, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int fb0_offset_addr, unsigned int fb1_offset_addr,
        unsigned int fb2_offset_addr, ap_uint<2> & active_frame,
        ap_uint<1> mode){
#pragma HLS INTERFACE s_axilite port=mode
#pragma HLS INTERFACE ap_none register port=active_frame
#pragma HLS INTERFACE s_axilite port=fb0_offset_addr
#pragma HLS INTERFACE s_axilite port=fb1_offset_addr
#pragma HLS INTERFACE s_axilite port=fb2_offset_addr
#pragma HLS INTERFACE m_axi depth=9216 port=in offset=slave
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index;
    static int n = 0;

    if (mode == DMA_WRITE_MODE){
        n = (int)active_frame;
    }else{
        n++;
        if (n > 2)
            n = 0;
    }

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

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


DMA_Read_offset_tb.cpp を示す。

// DMA_Read_offset_tb.cpp
// 2016/07/15 by marsee
// 2017/03/16 : 3フレーム処理から1フレーム処理に変更
//

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

#include "DMA_Read.h"
#include "bmp_header.h"

int DMA_Read_offset(volatile int *in, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int fb0_offset_addr, unsigned int fb1_offset_addr,
        unsigned int fb2_offset_addr, ap_uint<2> & active_frame,
        ap_uint<1> mode);

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > outs;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;

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

    if ((fbmpr = fopen("test.bmp""rb")) == NULL){ // test.bmp をオープン
    //if ((fbmpr = fopen("road_1.bmp", "rb")) == NULL){ // test.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 =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

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

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }

    // 3 つのフレームバッファにそれぞれ'A' を入力する(1つに変更)
    memcpy(frame_buffer, rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    memcpy((int *)((unsigned int)frame_buffer + bmpihr.biHeight * bmpihr.biWidth * sizeof(int)),
        rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    memcpy((int *)((unsigned int)frame_buffer + 2 * bmpihr.biHeight * bmpihr.biWidth * sizeof(int)),
        rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    active_frame = 1// fb0_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    active_frame = 2// fb1_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    active_frame = 0// fb2_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    // outs ストリームのデータを buf に入力する
    for (int k=0; k<3; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(int i=0; i < bmpihr.biWidth; i++){
                outs >> vals;
                ap_int<32> val = vals.data;
                buf[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i] = (int)val;

                if (val != frame_buffer[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i]){
                    printf("ERROR HW and SW results mismatch k = %d, i = %d, j = %d, HW = %08x, SW = %08x\n", k, i, j, (int)val, (int)frame_buffer[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i]);
                    return(1);
                }
            }
        }
    }

    // DMAされたデータをBMPフィルに書き込む
    char output_file[] = "dma_result0.bmp";
    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        if ((fbmpw=fopen(output_file, "wb")) == NULL){
            fprintf(stderr, "Can't open %s by binary write mode\n", output_file);
            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 データの書き込み、逆順にする
        int offset = i * bmpihr.biWidth * bmpihr.biHeight;
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }
    free(rd_bmp);
    free(frame_buffer);
    return 0;
}

  1. 2017年04月18日 05:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのAXI4マスタ機能のオプションの違いによるテストベンチからの呼び出し方法

ちょっと訳があって、Vivado HLSのAXI4マスタ機能のオプションの違いによるテストベンチからの呼び出し方法を書いておく。

AXI4マスタ・インターフェースを生成するプラグマは

#pragma HLS INTERFACE m_axi port=<ポート名>

で、オプションにオフセット・アドレスを指定する offset がある。

この offset オプションには3つの設定がある。

off : ベース・アドレス無し。アドレスは 0x00000000
direct : ベース・アドレスは32ビット幅のポートの値となる。
slave : ベース・アドレスを示すためにAXI4-Liteインターフェースに32ビット幅のレジスタが追加される。この場合は、ブロックレベルのインターフェースをAXI4-Liteにする必要がある。
(混同を避けるため、こちらの用語はベース・アドレスにした)


なお、「Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.4) 2016 年 11 月 30 日」の114ページの「AXI4 インターフ ェイスのアドレス オフセットの制御」を参照している。

direct オプションの場合はポートでアドレスを与えるので、決まってしまうが、off と slave の場合のテストベンチでのアドレスの与え方の考え方を書いておこう。(私独自なのかもしれないが。。。)

例にとるのは、「Vivado HLS で DMA Write IP を作る2(絶対アドレス指定編)」と「Vivado HLS で DMA Write IP を作る(オフセット・アドレス指定編)」を例にとって説明しよう。

何を問題にしているか?を最初に書いておくと、ソフトウェアでのテストベンチのアドレスの与え方とハードウェアのIPとしたときのアプリケーションソフトでのアドレスの与え方の整合性なのだ。
DMA Write IP では、画像のフレームバッファを3つ用意して、3つアドレスに順番に順番にWrite していく。それは、画像を表示したときにジャギーを出さない標準的な方法だ。これを実現するために、1つの呼び出しで3回異なるアドレスにAXI4マスタ・インターフェースを使用して3回のDMA Write を行う。
その3つのアドレスをどう指定するか?なのだが、絶対アドレス3つを指定するのと、1つの絶対アドレスを指定して、そのオフセット・アドレスを3つ指定する方法が考えられる。たとえ、最初のオフセット・アドレス0だとしても、3つ指定することになる。
この2つの指定方法の差を説明すると、最初は、3つの独立したアドレスを指定するのだが、2番目は1つの独立したアドレスとその3つのオフセット・アドレスを指定するという違いがある。
最初の「絶対アドレスを3つ指定する方法」では、指定するアドレスは3つなのだが、「1つの絶対アドレスを指定して、そのオフセット・アドレスを3つ指定する方法」では、4つのアドレスを指定する必要がある。

Vivado HLS で DMA Write IP を作る(絶対アドレス指定編)」では、最初の「絶対アドレスを3つ指定する方法」で書いたDMA Write IP となっている。
関数の定義は、

int DMA_Write(hls::stream >& ins, volatile int *out,
  unsigned int frame_buffer0, unsigned int frame_buffer1,
  unsigned int frame_buffer2, volatile ap_uint<2> & active_frame)

なのだが、指示子は以下のようになっている。

#pragma HLS INTERFACE ap_vld 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=out offset=off
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE s_axilite port=return

out ポートのAXI4マスタ・インターフェースのoffset オプションは off としている。つまり、このベース・アドレスは、0 で、frame_buffer0、frame_buffer1、frame_buffer2 を絶対アドレスとして設定して、それらのアドレスを指定してDMAすることになる。
offset オプションを off とする実装での、C コードの合成結果のレジスタマップを見てみよう。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of frame_buffer0
//        bit 31~0 - frame_buffer0[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of frame_buffer1
//        bit 31~0 - frame_buffer1[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of frame_buffer2
//        bit 31~0 - frame_buffer2[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


上のレジスタのアドレスマップでは、ベース・アドレスを設定するレジスタ設定が存在しない。つまり、テストベンチでDMA_Write() を呼ぶときに適当な呼び方は、ベース・アドレスを 0 とする呼び方になる。つまり、offset オプションを off としたときのベース・アドレスが 0x00000000 になるというVivado HLS の規則に沿った関数コールの方法となる。そのDMA_Write() 関数のテストベンチでの呼び出し部分を下に示す。

DMA_Write(ins, (volatile int *)0, (unsigned int)frame_buffer,
 (unsigned int)frame_buffer+(bmpihr.biWidth * bmpihr.biHeight * sizeof(unsigned int)),
 (unsigned int)frame_buffer+(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(unsigned int)),
 active_frame);


ここでハードウェアの場合を考えてみると、ベース・アドレスは上のレジスタ・マップに無いように設定する必要が無く、frame_buffer0、frame_buffer1、frame_buffer2 の3つの絶対アドレスだけを指定すればよいことになり、関数コールのやり方とあっている。

下の関数コールのように読んでしまうと、ベース・アドレスと、3つの絶対アドレスを指定することになり、上のアドレスマップでは設定できないことになってしまう。

DMA_Write(ins, (volatile int *)frame_buffer, (unsigned int)0,
 (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
 (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
 active_frame);


この関数コールを使う場合は、ベース・アドレスにframe_buffer の値を使っている。そしてのベース・アドレスからのオフセット・アドレスを3つ設定している。これは、ハードウェアにすると4つのレジスタを設定することになる。AXI4マスタ・インターフェースのoffset オプションは off にしていたのでは、4つのレジスタを設定することはないので、offset オプションは slave にする必要がある。こうすれば、ベース・アドレス+オフセット・アドレスを3つ設定することができる。
Vivado HLS で DMA Write IP を作る(オフセット・アドレス指定編)」の場合のアドレスマップを示す。この実装では、AXI4マスタ・インターフェースのoffset オプションは slave になっている。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of out_r
//        bit 31~0 - out_r[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of fb0_offset_addr
//        bit 31~0 - fb0_offset_addr[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of fb1_offset_addr
//        bit 31~0 - fb1_offset_addr[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of fb2_offset_addr
//        bit 31~0 - fb2_offset_addr[31:0] (Read/Write)
// 0x34 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


0x18に outr のレジスタが設置されている。
  1. 2017年04月15日 20:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で DMA Write IP を作る2(絶対アドレス指定編)

Vivado HLS で DMA Write IP を作る(絶対アドレス指定編)”のテストベンチが間違っていたので、その修正です。その他のC ソースコードも修正してあります。

まずは、Source の DMA_Write.c を貼っておきます。

// DMA_Write.cpp
// 2016/07/10 by marsee
//
// frame_buffer0, frame_buffer1, frame_buffer2 には3つのフレームバッファのアドレスを入れる
// 2017/04/15 : m_axiプラグマのdepthを変更した
//

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

#include "DMA_Write.h"

int DMA_Write(hls::stream<ap_axis<32,1,1,1> >& ins, volatile int *out,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        unsigned int frame_buffer2, volatile ap_uint<2> & active_frame){
#pragma HLS INTERFACE ap_vld 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=9216 port=out offset=off
//#pragma HLS INTERFACE m_axi depth=30000000 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;
            case 1 :
                dma_index = frame_buffer1/sizeof(int);
                break;
            case 2 :
                dma_index = frame_buffer2/sizeof(int);
                break;
        } 
        active_frame = 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;
}


次に、DMA_Write.h を貼っておきます。

// DMA_Write.h
// 2016/07/10 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    3

#endif


DMA_Write_tb.cpp を貼っておきます。

// DMA_Write_tb.cpp
// 2016/07/10 by marsee
//
// 2017/04/15 : DMA_Write()を呼び出すときの引数を変更した
//

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

#include "DMA_Write.h"
#include "bmp_header.h"

int DMA_Write(hls::stream<ap_axis<32,1,1,1> >& ins, volatile int *out,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        unsigned int frame_buffer2, volatile ap_uint<2> & active_frame);

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    ap_axis<32,1,1,1> pix;

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

    if ((fbmpr = fopen("test.bmp""rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(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 =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

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

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

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

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

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

                ins << pix;
            }
        }
    }

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }

    DMA_Write(ins, (volatile int *)0, (unsigned int)frame_buffer,
        (unsigned int)frame_buffer+(bmpihr.biWidth * bmpihr.biHeight * sizeof(unsigned int)),
        (unsigned int)frame_buffer+(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(unsigned int)),
        active_frame);
    
    // DMAされたデータをBMPフィルに書き込む
    char output_file[] = "dma_result0.bmp";
    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        if ((fbmpw=fopen(output_file, "wb")) == NULL){
            fprintf(stderr, "Can't open %s by binary write mode\n", output_file);
            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 データの書き込み、逆順にする
        int offset = i * bmpihr.biWidth * bmpihr.biHeight;
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }       
    free(rd_bmp);
    free(frame_buffer);
    return 0;
}


これで、C コードの合成を行いました。Vivado HLS のバージョンは 2016.4 です。
結果を示します。
DMA_Write_IP2_1_170415.png

DMA_Write_IP2_2_170415.png
2016.2 の結果と比べると多少、リソース使用量が減っていますね。

合成されたAXI4 Lite SlaveのVerilog HDL ファイルのDMA_Write_AXILiteS_s_axi.v のアドレスマップを下に示します。
ここでのポイントは、 DMA_Write() 関数自体のオフセット・アドレスを示すレジスタは無いことです。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of frame_buffer0
//        bit 31~0 - frame_buffer0[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of frame_buffer1
//        bit 31~0 - frame_buffer1[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of frame_buffer2
//        bit 31~0 - frame_buffer2[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


C/RTL協調シミュレーションを行いました。
DMA_Write_IP2_3_170415.png
エラーが発生してしまいました。

次に、DMA_Write.cpp の

#pragma HLS INTERFACE m_axi depth=9216 port=out offset=off

のdepth だけを 30000000 に変更します。
DMA_Write_IP2_4_170415.png

Cコードの合成を行います。depth の値は合成結果に影響を及ぼさないので、結果の変化はありません。
C/RTL協調シミュレーションを行いました。今度は、正常終了です。
DMA_Write_IP2_5_170415.png

C/RTL協調シミュレーションの波形を見ていきます。
まずは波形全体です。
DMA_Write_IP2_6_170415.png

DMA_Write_IP2_7_170415.png
出力のAXI4 Master Write も正常にデータが出いているのが分かります。

レジスタ設定用のAXI4-Lite Slave アクセスを見てみましょう。
DMA_Write_IP2_8_170415.png
3つのDMAのWrite の先頭アドレスはそれぞれ 0x0133ad98, 0x1133dd98, 0x01340d98 です。

3つの DMA Write の先頭アドレスを見てみましたが、見事に、0x0133ad98, 0x1133dd98, 0x01340d98 でした。
1つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_9_170415.png

2つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_10_170415.png

3つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_11_170415.png

malloc() で確保した画像領域の最後+1 は 0x01340d98 + 64(10進数)x 48(10進数) x 4(バイト) = 0x01343d98 です。
因みに depth の 30000000 は、0x01c9c380 です。
テストベンチで DMA_Write() の out のオフセットに0 を入れています。(「Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.4) 2016 年 11 月 30 日」の114ページの「AXI4 インターフ ェイスのアドレス オフセットの制御」参照)
そして、何度かやってみたのですが、malloc() で取られた画像用のバッファのアドレス以上のdepth の値を与えないとエラーになってしまうようなのです。つまり、Vivado HLS のC/RTL協調シミュレーションの仕組みはアクセスする可能性のあるバッファ領域を確保しなくてはいけないようです。考えてみれば当たり前とも言えます。通常はオフセット・アドレス+オフセットですので、使う領域だけを depth に指定すれば良いのですが、malloc() された絶対アドレスを引数経由で渡してDMA するとこのような結果になります。

また、C/RTL協調シミュレーションの結果も奇妙なことになっています。
DMA_Write_IP2_12_170415.png

Cシミュレーションの結果である sim -> wrapc フォルダのdma_result0.bmp は正常に「A」ですが、
DMA_Write_IP2_13_170415.png

C/RTL協調シミュレーションの結果の sim -> wrapc_pc フォルダのdma_result0.bmp は真っ暗です。
DMA_Write_IP2_14_170415.png

このようなアドレスの与え方には、Vivado HLS が対応していないのかもしれません?
  1. 2017年04月15日 17:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ikwzm/vivado-hls-axi-dma-read-failureを試してみた

以前、

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション
「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション2
「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション3

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」をIP として入れたVivado プロジェクトを論理シミュレーションすると動作するが、 Post-Synthesis Functional Simulation すると動作しない。しかし、Project Settings のTarget language を VHDL にする(DMA Read IPはVerilog HDL で IP化しているのだが)と Post-Synthesis Functional Simulation が動作するという現象があった。それを ikwzm さんが vivado-hls-axi-dma-read-failure でテストケースを作ってくれたのだが、Linux にVivado 2016.4 の環境が無かったためやっていなかった。今回は、環境を構築したためやってみた。

まずは、私のVirtualBox 上で vivado-hls-axi-dma-read-failureに書いてある通りにGitHub からクローンした(Ubuntu 16.04、Vivado 2016.4)
git clone https://github.com/ikwzm/vivado-hls-axi-dma-read-failure.git
cd vivado-hls-axi-dma-read-failure
git submodule init
git submodule update

vivado-hls-axi-read-failure_1_170411.png

これで、vivado-hls-axi-dma-read-failure ディレクトリが生成され、中にproject_post_synth_sim_ng ディレクトリとproject_post_synth_sim_ok ディレクトリがある。この中に create_project.tcl がある。
vivado-hls-axi-read-failure_2_170411.png

Vivado を起動した。
vivado-hls-axi-read-failure_3_170411.png

Vivado のGUI が立ち上がったので、まずは、tcl console で ~/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ng に移動する。
cd /home/masaaki/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ng/
vivado-hls-axi-read-failure_4_170411.png

souce create_project.tcl を実行した。
vivado-hls-axi-read-failure_5_170411.png

プロジェクトが生成された。
vivado-hls-axi-read-failure_6_170411.png

Flow Navigator からSynthesis -> Run Synthesis をクリックして、論理合成を行った。
論理合成が終了した。
vivado-hls-axi-read-failure_7_170411.png

Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
Vivado Internal Exception が出てしまった。tcl console から close_sim -force でもシミュレーションが落ちなかったので、vivado のプロセスを kill した。
vivado-hls-axi-read-failure_8_170411.png

もう一度、Vivado を起動して、tcl console に
cd /home/masaaki/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ok/
souce create_project.tcl

を入力した。
vivado-hls-axi-read-failure_9_170411.png

論理合成を行った後、Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
Vivado Internal Exception が出てしまった。vivado のプロセスを kill した。
vivado-hls-axi-read-failure_10_170411.png

Linux でやるとうまく行かないので、vivado-hls-axi-dma-read-failure ディレクトリをそのままWindows 10 にコピーしてPost-Synthesis Functional Simulation をやってみた。
まずは、project_post_synth_sim_ng からやってみた。
もう一度、論理合成を行ってから、 Post-Synthesis Functional Simulation を行った。
vivado-hls-axi-read-failure_11_170411.png

100065 ns でタイムアウトで停止している。
vivado-hls-axi-read-failure_19_170411.png

波形を示す。
vivado-hls-axi-read-failure_12_170411.png
vivado-hls-axi-read-failure_13_170411.png
vivado-hls-axi-read-failure_14_170411.png

DMA Read IPのレジスタを設定するAXI4-Lite Slave アクセスのみだ。

次に、project_post_synth_sim_ok をやってみよう。
もう一度、論理合成を行ってから、Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
vivado-hls-axi-read-failure_15_170411.png

1636 ns で Failure: Simulation complete. で終了している。
vivado-hls-axi-read-failure_20_170411.png

波形を示す。
vivado-hls-axi-read-failure_16_170411.png
vivado-hls-axi-read-failure_17_170411.png
vivado-hls-axi-read-failure_18_170411.png

AXI4 Master も動作しているのが分かる。

(2017/04/12 : 追記)
Post-Synthesis Functional Simulation のOK とNG の違いですが、NG は Target language が Verilog です。
vivado-hls-axi-read-failure_21_170411.png

OK は、 Target language が VHDL です。
vivado-hls-axi-read-failure_22_170411.png

その違いで、論理合成がおかしくなるか?、正常か?が決まるようです。ちなみにDMA Read IP はVerilog HDLを指定してIP 化しています。
  1. 2017年04月11日 05:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション3

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション2”の続き。

前回は、Vivado HLS 2016.4 で Verilog でIP をExport RTL して、Vivado 2016.4 では、Target language を VHDL にしたら、Post-Synthesis Functional Simulation が正常になった。ここでは、IO ピンが足りるようにVirtex7 の xc7vx980tffg1928-2 を使用していたが、IO ピンが足りないZYBO (xc7z010clg400-1)ではどうか?を確かめてみた。ikzwm さんがやってくれたので、大丈夫とは思うが自分でも確かめてみる。

まずは、Vivado HLS 2016.4 で、”Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”のソースコードで、xc7z010clg400-1 をターゲットとして、Verilog でExport RTL を行った。
DMA_Read_IP_29_170319.png

そのVivado HLS2016.4 のDMA Read IPを使用して、Project part を xc7z010clg400-1 に変更し、Project Settings のTarget language を VHDL にした Vivado 2016.4 プロジェクトを作った。
DMA_Read_IP_30_170319.png

論理合成を行った。IO ピンはオーバーフローになっている。
DMA_Read_IP_31_170319.png

Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
DMA_Read_IP_32_170319.png
問題なく波形が出ている。

次に、念のため、Flow Navigator の Simulation -> Run Simulation -> Run Behavioral Simulation を選択して、論理シミュレーションを行った。
DMA_Read_IP_33_170319.png
こっちも問題なく波形が出ている。

よって、ZYBO (xc7z010clg400-1)でも問題なく波形が出ている。
論理合成で IO ピンがオーバーフローでも Post-Synthesis Functional Simulation には影響ないことが分かった。
  1. 2017年03月19日 06:22 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション2

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション”の続き。

ikwzm さんにVivado HLS のExport RTL のEvaluate Generated RTL をVHDL にしたら論理合成後の機能シミュレーションも動いたということでやってみたのだが、やはり結果は同様に動作していなかった。

Vivado RTL Synthesis のチェックを外して、Evaluate Generated RTL をVerilog でIP 化してやってみても同じだった。もう一度、Vivado RTL Synthesis のチェックを外して、Evaluate Generated RTL をVHDL でIP 化しても同様にダメだった。
DMA_Read_IP_24_170316.png

ikwzm さんからもう一度、教えてもらったところ、「Vivado-HLS の Export RTL as IP は Verilog かつ、Vivado の Target Language を VHDL にした場合」とのことだった。
もう一度、Vivado HLS 2016.4 で Evaluate Generated RTLVerilog に設定して、Export RTL を行った。
Vivado RTL Synthesis のチェックは外してあるが、IP になった後のファイルを見てみたところ、特に合成結果が入っているようには見えなかったので、やってみるだけなのだと思う。
DMA_Read_IP_25_170316.png

Vivado 2016.4 で DMA Read IP をリプレースして、Project Settings のTarget language VHDL にした。
DMA_Read_IP_26_170316.png

これで、論理合成(Run Synthesis)を行ってから、Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
やった~。波形が出た。。。
DMA_Read_IP_27_170316.png

DMA_Read_IP_28_170316.png

何でだろう?これで何で波形が出るの?
ともかくよかった。これはXilinx社にバグレポートしないと。。。

論理シミュレーションも問題なく波形が表示されている。。。
  1. 2017年03月18日 05:15 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション

Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”の続き。

前回は絶対アドレス指定のDMA Read IPをVivado HLS 2016.4 で作成した。今回は、そのDMA Read IPをVivado で論理シミュレーションと合成後の機能シミュレーションを行った。

前回やったときの記事は、”DMA Read IP を単体でシミュレーション3(DMA Read IP単体で論理合成後にシミュレーション)

このプロジェクトを使用した。
DMA_Read_IP_9_170316.png

ただし新しく生成した DMA Read IPに入れ替えてある。それに、DMA Read IPのレジスタ設定を行う reg_write_read IP が一度設定を終了しても、また設定を繰り返し行っていたので、修正を行った。上に示す図は論理合成が終了している。
論理合成後のレポートを示す。
DMA_Read_IP_10_170316.png

IOポートはすべて割り振られている。これで論理合成後の機能シミュレーションは問題ない。しかし、IOがオーバーフローしていても論理合成後の機能シミュレーションがうまく行くのかもしれない?確実に動作する回路で試していないので、よくわからない?

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

DMA Read IPとAXI Interconnect 2つだけのシンプルな構成だ。

Address Editor を示す。
DMA_Read_IP_12_170316.png

テストベンチとして、DMA Read IPのレジスタを設定する reg_write_read IP と AXI4 Slave BFM をメモリとして接続している。そのブロック図を下に示す。
DMA_Read_IP_13_170316.png

さて次に、論理シミュレーションを行う。
Flow Navigator の Simulation -> Run Simulation -> Run Behavioral Simulation を選択する。
DMA_Read_IP_14_170316.png

論理シミュレーション結果の全体波形を示す。
DMA_Read_IP_15_170316.png
波形1

DMA_Read_IP_17_170316.png
波形2

波形の数が多いので、2つの画像になってしまった。最初の画像は主にDMA Read IPのAXI4 Master Read アクセスが表示されている。2つ目の波形には、DMA Read IPのレジスタを設定するAXI4-Lite Slave アクセスとAXI4-Stream 出力が表示されている。

最初の波形を拡大してみよう。
DMA_Read_IP_16_170316.png
波形3

バーストに1クロックの Wait はあるがほとんどフルバーストでAXI4 Master Read が行われている。

次に、DMA Read IPを設定するのレジスタを設定するAXI4-Lite Slave アクセスを下に示す。
DMA_Read_IP_18_170316.png
波形4

まずは、DMA Read IPのレジスタマップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of frame_buffer0
//        bit 31~0 - frame_buffer0[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of frame_buffer1
//        bit 31~0 - frame_buffer1[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of frame_buffer2
//        bit 31~0 - frame_buffer2[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of mode_V
//        bit 0  - mode_V[0] (Read/Write)
//        others - reserved
// 0x34 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


レジスタを設定する reg_write_read IP の設定を示す。

// reg_write_read.h
// 2016/06/10 by marsee
//
// reg_addr_data フォーマット、
// 第1フィールド(reg_ad[x][0]):Write - 0, Read - 1
// 第2フィールド(reg_ad[x][1]):Delay、単位はクロック数
// 第3フィールド(reg_ad[x][2]):アドレス、16進数
// 第4フィールド(reg_ad[x][3]):データ、16進数
//
// 終了は第2フィールド:Delayに 0xffffffff が書いてあったとき

#define AD_ARRAY_LIMIT 256
#define REG_WRITE 0
#define REG_READ 1

#define R_W_FIELD 0
#define DELAY_FIELD 1
#define ADDRESS_FIELD 2
#define DATA_FIELD 3

const unsigned int reg_ad[AD_ARRAY_LIMIT][4]={
        {000x44A00018, 0x10000000},
        {000x44A00020, 0x10000000},
        {000x44A00028, 0x10000000},
        {000x44A00030, 0},
        {100x44A00000, 0},
        {000x44A00000, 1},
        {00xfffffffe, 00},
        {00xffffffff, 00},


つまり、0x44A00018、0x44A00020、0x44A00028 に 0x10000000 を書いているが、3つのフレームバッファのアドレスを書いている。
0x44A00030 でDMA Read IPのモードを書いている。 0 なので、DMA_WRITE_MODE だ。
0x44A00000 をRead してから、0x44A00000 に 1 を書いてDMA Read IPをスタートさせている。

DMA Read IPのAXI4-Stream 出力の波形を示す。
DMA_Read_IP_19_170316.png
波形5

前に示した全体波形では、outs_tlast が時々 1 変化していたのが見えたと思うが、画像フレームのスタート時には、outs_tuser が 1 クロック間だけ 1 となっているのがわかる。
と言う訳で完全に動作しているようだ。

次に、論理合成後の機能シミュレーションを行う。
Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択する。

Post-Synthesis Functional Simulation での全体波形を示す。
DMA_Read_IP_21_170316.png
波形6

DMA_Read_IP_22_170316.png
波形7

やはり、Post-Synthesis Functional Simulation で波形が出てこない。

DMA Read IPのレジスタを設定するAXI4-Lite Slave アクセスを拡大してみよう。
DMA_Read_IP_23_170316.png
波形8

こちらは問題ないようだ。
  1. 2017年03月17日 05:32 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»