FC2カウンター FPGAの部屋

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

FPGAの部屋

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

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を使用してカメラの画像を表示

「”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)」では、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路を使用して、カメラ画像を表示することができなかった。これは、PS から出力される fclk の出力周波数が設定されていないのが原因ではないか?というご指摘を ikwzm さんから頂いた。
そこで、「ikwzm さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法」で、デバイスツリー・オーバーレイを使用した。fclk の設定のやり方を学習したので、実際にFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路に適用して試してみよう。

まずは、「”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる7(shellスクリプト)」で作成した devtov スクリプトを変更した。
PYNQ_Linux_ikwzm_107_170419.png

#!/bin/sh
# device tree overlay command (devtov)
# Be sure to run it as superuser
# $1 is device tree name

mkdir /config/device-tree/overlays/$1
cp $1.dtbo /config/device-tree/overlays/$1/dtbo
echo 1 > /config/device-tree/overlays/$1/status

sleep 0.1
chmod 666 /dev/uio*
ls -l /dev/uio*

if test -e /dev/udmabuf*; then
    chmod 666 /dev/udmabuf*
    ls -l /dev/udmabuf*
fi

if test -e /dev/fclk0; then
    chmod 666 /dev/fclk*
    ls -l /dev/fclk*
fi


次に、「ikwzm さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法」を参考に fclk のエントリを追加したデバイスツリー・オーバーレイの pynq_fastx_fclk.dts を作成した。
PYNQ_Linux_ikwzm_108_170419.png

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            #address-cells = <0x1>;
            #size-cells = <0x1>;
            axi_iic_0@41600000 {
                compatible = "generic-uio";
                reg = <0x41600000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axi_vdma_0@43000000 {
                compatible = "generic-uio";
                reg = <0x43000000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_0@43C10000 {
                compatible = "generic-uio";
                reg = <0x43C10000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_1@43C20000 {
                compatible = "generic-uio";
                reg = <0x43C20000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            bitmap_disp_cntrler_axi_master_0@43C00000 {
                compatible = "generic-uio";
                reg = <0x43C00000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            fastx_corner_det_0@43C30000 {
                compatible = "generic-uio";
                reg = <0x43C30000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            lap_filter_axis_0@43C50000 {
                compatible = "generic-uio";
                reg = <0x43C50000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            mt9d111_inf_axis_0@43C40000 {
                compatible = "generic-uio";
                reg = <0x43C40000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            unsharp_mask_axis_0@43C60000 {
                compatible = "generic-uio";
                reg = <0x43C60000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            udmabuf4 {
                compatible = "ikwzm,udmabuf-0.10.a";
                minor-number = <4>;
                size = <0x00600000>;
            };
            fclk0 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 15>;
            };
            fclk1 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 16>;
            };
            fclk2 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 17>;
            };
        };
    };
};


dtc -I dts -O dtb -o pynq_fastx_fclk.dtbo pynq_fastx_fclk.dts
で dts をコンパイルして、pynq_fastx_fclk.dtbo を生成した。
PYNQ_Linux_ikwzm_109_170419.png

スーパーユーザーになって、
./devtov pynq_fastx_fclk
で、デバイスツリー・オーバーレイをロードした。uio, udmabuf, fclk のデバイスツリー・オーバーレイがロードされた。
PYNQ_Linux_ikwzm_110_170419.png

/sys/class/fclkcfg/fclk0 ディレクトリに移動して fclk0 のステータスを見た。
100 MHz に設定されていた。 enable = 1 なので、すでに出力されている。
cd /sys/class/fclkcfg/fclk0
cat rate
cat round_rate
cat enable

PYNQ_Linux_ikwzm_111_170419.png

/sys/class/fclkcfg/fclk1 ディレクトリに移動して fclk1 のステータスを見た。
rate を 25 MHz に設定した。
cd ../fclk1
cat rate
cat round_rate
cat enable
su
echo 25000000 > round_rate
cat round_rate
echo 25000000 > rate
cat rate
exit

PYNQ_Linux_ikwzm_112_170419.png

/sys/class/fclkcfg/fclk2 ディレクトリに移動して fclk2 のステータスを見た。
rate を 72 MHz に設定したが、round_rate の結果をみると、71.428572 MHz だった。
cd ../fclk2
cat rate
cat round_rate
cat enable
su
echo 72000000 > round_rate
cat round_rate
echo 72000000 > rate
cat rate
exit

PYNQ_Linux_ikwzm_113_170419.png

~/device_tree_overlay/ ディレクトリに戻って、pynq_fastx_wrapper.bit をPYNQ ボードにコンフィギュレーションした。
cd ~/device_tree_overlay/
ls
su
./fpgamag pynq_fastx_wrapper.bit
exit


”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)」の cam_disp.c の表示が出ないように書き換えて、コンパイルを行った。
cam_disp を起動した。
gcc -o cam_disp cam_disp.c
./cam_disp

PYNQ_Linux_ikwzm_114_170419.png

すると、カメラ画像を表示することができました。(部屋が散らかっていて、お見苦しい点をお詫びいたします)
PYNQ_Linux_ikwzm_115_170419.jpg

最後に、修正した cam_disp.c を貼っておきます。

// cam_disp.c
// 2017/04/08 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.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 ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#define FASTX_THRESHOLD     20

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()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8;
    int fd_udmabuf, fd_paddr;
    volatile unsigned int *axi_iic_0, *axi_vdma_0, *axis_switch_0, *axis_switch_1;
    volatile unsigned int *bitmap_disp_cntrler_axim_0, *fastx_corner_det_0;
    volatile unsigned int *mt9d111_inf_axis_0;
    volatile unsigned int *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    // axi_iic_0 (uio0)
    fd0 = open("/dev/uio0", O_RDWR); // axi_iic_0
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (axi_iic_0) open errorn");
        exit(-1);
    }
    axi_iic_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (axi_iic_0 == MAP_FAILED){
        fprintf(stderr, "axi_iic_0 mmap errorn");
        exit(-1);
    }
    
    // axi_vdma_0 (uio1)
    fd1 = open("/dev/uio1", O_RDWR); // axi_vdma_0
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (axi_vdma_0) open errorn");
        exit(-1);
    }
    axi_vdma_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (axi_vdma_0 == MAP_FAILED){
        fprintf(stderr, "axi_vdma_0 mmap errorn");
        exit(-1);
    }

    // axis_switch_0 (uio2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open errorn");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (axis_switch_0 == MAP_FAILED){
        fprintf(stderr, "axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // axis_switch_1 (uio3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open errorn");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (axis_switch_1 == MAP_FAILED){
        fprintf(stderr, "axis_switch_1 mmap errorn");
        exit(-1);
    }
    
    // bitmap_disp_cntrler_axim_0 (uio4)
    fd4 = open("/dev/uio4", O_RDWR); // bitmap_disp_cntrler_axim_0
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (bitmap_disp_cntrler_axim_0) open errorn");
        exit(-1);
    }
    bitmap_disp_cntrler_axim_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (bitmap_disp_cntrler_axim_0 == MAP_FAILED){
        fprintf(stderr, "bitmap_disp_cntrler_axim_0 mmap errorn");
        exit(-1);
    }
    
    // mt9d111_inf_axis_0 (uio7)
    fd7 = open("/dev/uio7", O_RDWR); // mt9d111_inf_axis_0
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (mt9d111_inf_axis_0) open errorn");
        exit(-1);
    }
    mt9d111_inf_axis_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (mt9d111_inf_axis_0 == MAP_FAILED){
        fprintf(stderr, "mt9d111_inf_axis_0 mmap errorn");
        exit(-1);
    }
    
    // udmabuf4
    fd_udmabuf = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd_udmabuf == -1){
        fprintf(stderr, "/dev/udmabuf4 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, (ALL_DISP_ADDRESS*3), PROT_READ|PROT_WRITE, MAP_SHARED, fd_udmabuf, 0);
    if (frame_buffer == MAP_FAILED){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // phys_addr of udmabuf4
    fd_paddr = open("/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);
    
    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x0// 0x40 = 0
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000, disable
    axis_switch_1[19] = 0x80000000// 0x4C = 0x80000000, disable
    axis_switch_1[0] = 0x2// Comit registers
    
    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x0// 0x40 = 0;
    axis_switch_0[0] = 0x2// Comit registers

    // AXI VDMA Initialization sequence (axi_vdma_0)
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    axi_vdma_0[41] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    axi_vdma_0[42] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    axi_vdma_0[44] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 2
    axi_vdma_0[45] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 3
    axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    axi_vdma_0[40] = VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(axi_iic_0);
    
    cam_i2c_write(axi_iic_0, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(axi_iic_0, 0xba, 0x970x20);    // RGB Mode, RGB565

    mt9d111_inf_axis_0[1] = 0;
    mt9d111_inf_axis_0[0] = (unsigned int)phys_addr;

    bitmap_disp_cntrler_axim_0[0] = (unsigned int)phys_addr;
        
    munmap((void *)axi_iic_0, 0x10000);
    munmap((void *)axi_vdma_0, 0x10000);
    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)bitmap_disp_cntrler_axim_0, 0x10000);
    munmap((void *)mt9d111_inf_axis_0, 0x10000);
    munmap((void *)frame_buffer, (ALL_DISP_ADDRESS*3));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd7);
    close(fd_udmabuf);
}

  1. 2017年04月19日 04:55 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

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

Thinkpad 13が届きました

2017/04/17 金曜日に新しいノートパソコンのThinkpad 13 が届きました。
「PYNQ祭り」延長戦 : FPGAディープラーニング実践懇親会”でがっつり実装するために購入しました。大赤字です。。。w

CPU は i5-7200、メモリ 16GB、SSD 512GB です。メモリは 16 GB 1枚なので、もう1枚実装すると 32 GB になります。かなりハイスペックですね。CPUが多少しょぼいですが。。。

古い世代には憧れのThinkpad ということで、キーボードやトラックパッドは良い感じです。
ThinkPad_13_1_170417.jpg

早速、Vivado 2016.4 と SDSoC 2016.4 をインストールしました。SDSoCのライセンスについてはひでみさんの本のAGPF WORKS に書いてある通りでした。満足です。
ThinkPad_13_2_170417.jpg

今まで使っていたマウスコンピューターのノートパソコンはVivado などをアンインストールして、奥さん用にします。
今回購入したThinkpad 13 はスペックが良いので、第2のVivado コンパイルマシンとしても活用したいと思います。
  1. 2017年04月17日 05:30 |
  2. パソコン関連
  3. | トラックバック:0
  4. | コメント:0

かすみがうらマラソン2017に出場しました

今日は、かすみがうらマラソン2017の 5 km に出場してきました。

午前6時45分頃家を出て、かすみがうらマラソンの臨時駐車場に車を入れました。たくさんの車がありました。
kasumigaura_marathon_1_170416.jpg

臨時駐車場からかすみがうらマラソンの出発地点の近くまでは、シャトルバスが出ています。
kasumigaura_marathon_2_170416.jpg

お世話になった、阿見アスリートクラブ、JCOMテントの近くの様子です。たくさんの人とたくさんの臨時トイレがあります。トイレの量も多く、また、小便器の仮設トイレもあったので、トイレの行列が長くなかったのは良かったです。
kasumigaura_marathon_3_170416.jpg

kasumigaura_marathon_4_170416.jpg

kasumigaura_marathon_5_170416.jpg

マッサージのテントなども近くにありました。無料でマッサージをやってもらえますよ。

私は、花粉症なので、5 km に出場したのですが、マスクを3枚持って走りました。2 km ごとに交換しようと思っていました。去年はそうしたんですが、今日の気温は26度、湿度は60% 以上だったので、とっても苦しくなって、途中でマスクを取って走りました。

結果は、5 km オフィシャルタイムで、23分32秒、プライベートタイムで 28分14秒でした。順位は 184 位/614人中、50歳代では 32 位/ 114 人中でした。今日は守谷マラソンよりも 40秒程度遅かったです。。。orz
kasumigaura_marathon_6_170416.jpg
  1. 2017年04月16日 21:30 |
  2. 日記
  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 さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)”を解決する試み。

昨日のFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路が動かないと書いたが、ikwzm さんがクロックはどうなっているかと聞いてくれて、それでハッと気が付いた。Zynq のクロックはPSから供給されていて、ビットファイルは関係ない。。。
ikzwm さんから、”Linux でユーザー空間から Zynq の PLクロック信号を制御するデバイスドライバ”を教えて頂いた。いつもありがとうございます。しかし、何でも作ってありますね。。。
更に、今回のFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路用の設定も教えてもらった

ikwzm/fclk-install によると、デバイスツリー・オーバーレイを設定する dtbocfg.rb というコマンドがPYNQ ボードのDebian に入っているそうだ。
PYNQ_Linux_ikwzm_95_170414.png

ikwzm/fclk-install の 4 つの DTS ファイルを fclk0-zynq-pynq.dts、fclk1-zynq-pynq.dts、fclk2-zynq-pynq.dts、fclk3-zynq-pynq.dts ファイルとして、~/device_tree_overlay/fclk_install ディレクトリにセーブした。
PYNQ_Linux_ikwzm_96_170414.png

4 つの DTS ファイルのうちの fclk0-zynq-pynq.dts ファイルを示す。なお、これらのファイルはikwzm/fclk-install の fclk0-zynq-zybo.dts ~ fclk3-zynq-zybo.dts のコピーだ。
fclk0-zynq-pynq.dts を示す。

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            fclk0 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 15>;
            };
        };
    };
};


fclk1-zynq-pynq.dts は fclk0 が fclk1 に変更されている。
更に、
clocks      = <1 15>;

clocks      = <1 16>;
になっていて、+1 されている。これは、fclk2, fclk3 も同様だ。

さて、 fclk0-zynq-pynq.dts をデバイスツリー・オーバーレイとしてロードしてみよう。
スーパーユーザーになって、次のコマンドを実行した。
dtbocfg.rb --install --dts fclk0-zynq-pynq.dts fclk0
PYNQ_Linux_ikwzm_97_170414.png

シリアル接続のコンソールに fclk0 の表示が出た。
PYNQ_Linux_ikwzm_106_170414.png

/config/device-tree/overlays ディレクトリに fclk0 ディレクトリができた。
PYNQ_Linux_ikwzm_98_170414.png

fclk0 ディレクトリの中には、dtbo と status ファイルがあった。
PYNQ_Linux_ikwzm_99_170414.png

/sys/class/flckcfg ディレクトリには、fclk0 ディレクトリへのリンクがあった。
PYNQ_Linux_ikwzm_100_170414.png

/sys/class/flckcfg/fclk0 ディレクトリの内容を示す。
PYNQ_Linux_ikwzm_101_170414.png

enable, rate, round_rate ファイルをリードした。
PYNQ_Linux_ikwzm_102_170414.png

enable = 1, rate = 100000000 で 100 MHz だった。round_rate は値がおかしいというか、0 Hz の設定値だったら 251.953 KHz だよ、ということなんだろう?と思う。

試しにスーパーユーザーになって、100000000 を設定してみたところ、100000000 になった。
PYNQ_Linux_ikwzm_103_170414.png

試しに
echo 25000000 > rate
で、fclk0 を 25 MHz に設定してみた。
PYNQ_Linux_ikwzm_104_170414.png
  1. 2017年04月14日 05:12 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0