cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
logicool
Observer
Observer
895 Views
Registered: ‎12-29-2019

How to use AXI Master Image Processing IP as Userspace I/O Device

 

Target Device: Zybo-Z7-20 (xc7z020clg400-1)

Tool Version: Vivado 2019.2.1 / Vivado HLS 2019.2.1 / Petalinux 2019.2.1 / Vitis 2019.2.1

 

HLS of the following function generated a sobel filter IP that reads an RGB image as input, applies some image processing, and writes an EDGE image as output.

 

xf_sobel_filter.cpp

 

#include "xf_sobel_filter.hpp"

void axi_sobel(ap_uint<INPUT_PTR_WIDTH> *src, ap_uint<OUTPUT_PTR_WIDTH> *dst)
{

#pragma HLS INTERFACE m_axi     port=src    bundle=gmem1   offset=slave
#pragma HLS INTERFACE m_axi     port=dst    bundle=gmem2   offset=slave
#pragma HLS INTERFACE s_axilite port=src    bundle=control
#pragma HLS INTERFACE s_axilite port=dst    bundle=control
#pragma HLS INTERFACE s_axilite port=return bundle=control

	const int pROWS = HEIGHT;
	const int pCOLS = WIDTH;
	const int pNPC1 = NPC1;

	xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1>	  img_0;
	xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1>     img_1;
	xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1>     img_2;
	xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1>     img_3;
	xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1>     img_4;
	xf::Mat<XF_16UC1, HEIGHT, WIDTH, NPC1>    img_5;
	xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1>     img_6;
	xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1>	  img_7;

#pragma HLS stream variable=img_0.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_1.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_2.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_3.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_4.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_5.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_6.data depth=pCOLS/pNPC1
#pragma HLS stream variable=img_7.data depth=pCOLS/pNPC1
	
	img_0.rows = pROWS; img_0.cols = pCOLS;
	img_1.rows = pROWS; img_1.cols = pCOLS;
	img_2.rows = pROWS; img_2.cols = pCOLS;
	img_3.rows = pROWS; img_3.cols = pCOLS;
	img_4.rows = pROWS; img_4.cols = pCOLS;
	img_5.rows = pROWS; img_5.cols = pCOLS;
	img_6.rows = pROWS; img_6.cols = pCOLS;
	img_7.rows = pROWS; img_7.cols = pCOLS;

	#if FILTER_WIDTH==3
		float one_sigma = 0.5f;
	#endif
	#if FILTER_WIDTH==7
		float one_sigma = 1.16666f;
	#endif
	#if FILTER_WIDTH==5
		float one_sigma = 0.8333f;
	#endif
	int shift = 0;

#pragma HLS DATAFLOW

xf::Array2xfMat<INPUT_PTR_WIDTH,XF_8UC3,HEIGHT,WIDTH,NPC1>(src,img_0);

xf::bgr2gray<XF_8UC3, XF_8UC1, HEIGHT, WIDTH, NPC1>(img_0, img_1);
xf::GaussianBlur<FILTER_WIDTH, XF_BORDER_CONSTANT, XF_8UC1, HEIGHT, WIDTH, NPC1>(img_1, img_2, one_sigma);
xf::Sobel<XF_BORDER_CONSTANT, FILTER_WIDTH, XF_8UC1, XF_8UC1, HEIGHT, WIDTH, NPC1, XF_USE_URAM>(img_2, img_3, img_4);
xf::addWeighted(img_3,0.5,img_4,0.5,0.0,img_5);
xf::convertTo<XF_16UC1, XF_8UC1, HEIGHT, WIDTH, NPC1>(img_5, img_6, XF_CONVERT16UTO8U, shift);
xf::gray2bgr<XF_8UC1, XF_8UC3, HEIGHT, WIDTH, NPC1>(img_6, img_7);

xf::xfMat2Array<OUTPUT_PTR_WIDTH,XF_8UC3,HEIGHT,WIDTH,NPC1>(img_7,dst);
}

 

 

xf_sobel_filter.hpp

 

#include "hls_stream.h"
#include "ap_int.h"
#include "common/xf_common.h"
#include "common/xf_utility.h"
#include "common/xf_infra.h"

#include "imgproc/xf_gaussian_filter.hpp"
#include "imgproc/xf_sobel.hpp"
#include "imgproc/xf_cvt_color.hpp"
#include "imgproc/xf_duplicateimage.hpp"
#include "imgproc/xf_add_weighted.hpp"
#include "imgproc/xf_convert_bitdepth.hpp"

#define WIDTH 	1280
#define HEIGHT	720
#define FILTER_WIDTH 3
#define NPC1 XF_NPPC1
#define XF_USE_URAM false
#define XF_CONVERT16UTO8U  0
#define INPUT_PTR_WIDTH  32
#define OUTPUT_PTR_WIDTH 32

void axi_sobel(ap_uint<INPUT_PTR_WIDTH> *src, ap_uint<OUTPUT_PTR_WIDTH> *dst);

 

 

The original Vivado HLS project is Adam Taylor's Edge Detection IP with AXI4-Stream interfaces.

https://www.hackster.io/adam-taylor/fpga-based-edge-detection-using-hls-192ad2

 

I decided to use AXI Master because I would like to use the IP in Linux application as UIO device.

 

Assuming this is a right direction, I have designed hardware in Vivado (and exported a *.xsa file).

Screenshot from 2020-04-07 15-19-56.png

 

Then, built linux image with petalinux such that my IP is registered as UIO device.

 

Confirmed Userspace I/O platform driver with generic IRQ handling is checked or set to <M>.

Screenshot from 2020-04-03 18-18-29.png

 

Auto-generated pl.dtsl

Screenshot from 2020-04-06 10-36-40.png

 

Modified system-user.dtsl

Screenshot from 2020-04-06 10-36-42.png

 

My boot args is set to:

console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio root=/dev/mmcblk0p2 rw rootwait

 

After that I confirmed two UIO devices recognized. 

Screenshot from 2020-04-07 15-47-17.png

 

I was able to control/blink 4LEDs on Zybo using AXI_GPIO IP using the following code.

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#define GPIO_DATA_OFFSET 0x0000
#define GPIO_TRI_OFFSET 0x0004

inline void gpio_write(void *gpio_base, unsigned int offset, unsigned int value)
{
	*((volatile unsigned *)(gpio_base + offset)) = value;
}

inline unsigned int gpio_read(void *gpio_base, unsigned int offset)
{
	return *((volatile unsigned *)(gpio_base + offset));
}

unsigned int get_memory_size(char *sysfs_path_file)
{
	FILE *size_fp;
	unsigned int size;

	// open the file that describes the memory range size that is based on the
	// reg property of the node in the device tree

	size_fp = fopen(sysfs_path_file, "r");

	if (!size_fp)
	{
		printf("unable to open the uio size file\n");
		exit(-1);
	}

	// get the size which is an ASCII string such as 0xXXXXXXXX and then be stop
	// using the file

	fscanf(size_fp, "0x%08X", &size);
	fclose(size_fp);

	return size;
}

int main(int argc, char **argv)
{
    printf("Hello World!\n");

    void* ptr;
    int fd;
    int gpio_size;

    /* Open device file for memory access */
    if ((fd = open("/dev/uio0", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }
    else
    {
    	printf("UIO0 opened.\n");
    }

    ptr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
    printf("Mmap call failure.\n");
    close(fd);
    return -1;
    }

    printf("%d\n", ptr);

	gpio_write(ptr, GPIO_TRI_OFFSET, 0x00);
    printf("Set GPIO_TRI Value.\n");
    while(1)
    {
        /* Set LEDs(PL GPIO) as High */
    	printf("Set LEDs(PL GPIO) as High.\n");
        gpio_write(ptr, GPIO_DATA_OFFSET, 0x0F);
        sleep(1);
        /* Set LEDs(PL GPIO) as Low */
    	printf("Set LEDs(PL GPIO) as Low.\n");
        gpio_write(ptr, GPIO_DATA_OFFSET, 0x00);
        sleep(1);
    }

    /* Release resources */
    munmap(ptr, 0x1000);
    close(fd);

    return 0;
}

 

 

Now, I have incomplete code to test AXI_SOBEL but I need to understand how this works in order to complete this.

 

/*
 * main.cpp
 *
 *  Created on: Apr 6, 2020
 *      Author: logicool
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

int main(int argc, char** argv)
{

	if(argc != 2)
	{
		fprintf(stderr, "Invalid Number of Arguments!\nUsage:\n");
		fprintf(stderr, "<Executable Name> <input image path> \n");
		return -1;
	}

    void* ptr;
    int fd;

    /* Open device file for memory access */
    if ((fd = open("/dev/uio1", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }
    else
    {
    	printf("UIO1 opened.\n");
    }

    ptr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
    printf("Mmap call failure.\n");
    close(fd);
    return -1;
    }

	cv::Mat src, dst;


	src=cv::imread(argv[1], 1);

	// reading in the color image
	if (src.data == NULL)
	{
		fprintf(stderr, "Cannot open image at %s\n", argv[1]);
		return 0;
	}

	/*

	This part needs to be developed. How can I run the filter?

	*/

	cv::imwrite("hls.bmp", dst);

    src.release();
    dst.release();
}

 

 

My questions are:

(1) Is this design to access and control AXI Master IP as UIO device is feasible?

(2) If so, how to do it in application?

 

I've been looking for examples but haven't found a complete solution yet.

 

Cheers,

0 Kudos
1 Reply
LGroth
Observer
Observer
245 Views
Registered: ‎05-13-2021

Did you find a solution? I'm stuck at the same problem

0 Kudos