1. 程式人生 > >ZYNQ HLS影象處理加速總結(二)——Processing System軟體部分

ZYNQ HLS影象處理加速總結(二)——Processing System軟體部分

概述

7010的硬核是兩個Cortex-A9,主頻666M(233333….),硬浮點+neon協處理器,效能不是很好,因為xilinx SDK可以生成底層IP的driver,所以PS裸跑起來很簡單,通過JTAG除錯很方便。初期時考慮到跑linux系統時的HLS IP的driver和VDMA的driver要寫核心模組,VDMA雖然在3.17的核心原始碼已經集成了驅動,但並沒有找到詳細的相關資料,也在xilinx community上問到有人說這個驅動很坑。於是打算用AMP,一個核跑linux負責上層的相關應用,一個核裸跑+ucosii或FreeRTOS負責操作AXI總線上的外設,然而想法總是很天真的。。。弄了好長時間AMP,把CPU1成功的掛起後,並不能再跑程式,問題始終也沒找到。不過在這期間也一直在進行linux下對IP核操作的相關嘗試。最終沒用AMP也解決了這個驅動的問題,下面簡單總結下遇到的部分問題

PS系統搭建簡述

參照前一篇文章中github裡的那個文件進行操作,檔案系統使用的是Linaro,在這推薦下xillybus,xillybus提供了完整的軟硬體工程,它提供的xillinux系統精簡的非常好的,系統從上電到登陸不到10秒,硬體工程除提供了HDMI/VGA顯示、音訊的IP(在xillinux系統裡已經集成了驅動),還提供了一個優化的HLS介面,可以按照網站上的步驟實現一個簡單的硬體加速的例子(然而並不能走AXI4-Stream匯流排)網站上還有多篇文件介紹devicetree和PCI-E。回到例子裡Linaro檔案系統,改動了好多地方,首先新增的bash自動補全,這個比較簡單不多說,linaro在使用者登陸後會執行一個檢查軟體更新的指令碼,這個指令碼執行時間巨長,導致每次ssh登陸都要等好長時間,一開始還能忍,後來用Qt Deploy的時候直接超時。。。然後在/etc/update-motd.d

路徑下找到了罪魁禍首(我不會告訴你我是硬找的)。這個路徑下有兩個指令碼是進行檢查更新操作的,直接註釋掉就好。然後配了一個smb方便傳輸檔案。最後把交叉編譯好的opencv和Qt庫拷進去就可以進行下一步了。

分配DDR給PL

這裡要注意,vdma進行資料搬運的時候需要確定的實體地址並有一定的預留空間,大家都知道linux是通過mmu將實體地址對映為虛擬地址然後給程式使用的,並且在軟體上成功申請一段大的連續的實體地址的記憶體概率是隨著系統執行時間的增加而降低的(記憶體越來越碎,dma傳輸要求是連續的),所以要在記憶體沒跑碎之前就留下一塊記憶體不給PS用,uboot可以設定linux執行使用的記憶體的大小,但和之前我用過的又有些不一樣,在早期的uboot裡只要設定”mem”env的值就好。(之前的核心沒有devicetree機制),一開始這麼試了一下,板子上的DDR為512M,我試了下”mem=510M”給PL預留了2M是可以的,但把mem改的更小的時候,核心就掛啦。。。參見之前寫的文章”

Zynq Reseving Physical Memory Issue“給出瞭解決方法,簡單的說就是要告訴uboot和核心:devicetree要放哪呀。(順便說下要想使uboot env的修改永久生效要重新編譯遍uboot,官方提供的uboot不帶SPI Flash的驅動)。

HLS生成的IP在linux下的操作

HLS在Export IP核的時候是也會“贈”一個軟體驅動的,這個驅動給出了查詢更改IP核狀態、讀寫功能暫存器的介面,並可以按照一個標準的流程初始化IP核,流程參見之前寫的”Vivado HLS —Processor Control“。這個驅動在linux下使用需要對devicetree做些小的更改。
首先要知道這個驅動是通過UIO(userspace I/O)核心模組進行操作的,需要核心配置為:(defconfig就是這個,檢查一下就好)

CONFIG_UIO=y
CONFIG_UIO_PDRV_GENIRQ=y

然後需要通過devicetree檔案告訴核心:我的外設裡有一個uio裝置,它在0x某某地址上。具體的操作是在 由SDK建立的devicetree工程中修改”compatible”的值,如在我的pl.dtsi檔案中

image_filter_0: [email protected]43c40000 {
            compatible = "xlnx,image-filter-1.1";
            interrupt-parent = <&intc>;
            interrupts = <0 35 4>;
            reg = <0x43c40000 0x10000>;
            xlnx,s-axi-control-bus-addr-width = <0x8>;
            xlnx,s-axi-control-bus-data-width = <0x20>;
        };

改為:

image_filter_0: [email protected]43c40000 {
            compatible = "generic-uio,uio";
            interrupt-parent = <&intc>;
            interrupts = <0 35 4>;
            reg = <0x43c40000 0x10000>;
            xlnx,s-axi-control-bus-addr-width = <0x8>;
            xlnx,s-axi-control-bus-data-width = <0x20>;
        };

重新生成dtb檔案後更新到板子上,就可以先測試下HLS生成的IP核能不能初始化了。

VDMA在linux下的操作

vdma的driver不好用,但找到了另一個方式——函式”mmap()”,這個函式可以將實體地址對映到虛擬地址空間上。
將vdma的基址對映到虛擬地址空間上,在linux系統下就可以直接通過指標訪問vdma的各個暫存器

   handle->vdmaVirtualAddress = (unsigned int*)mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)handle->baseAddr);

這個函式的返回值就是申請到的vdma基址的虛擬地址,這個地址加上暫存器offset就可以用來配置各個暫存器了,在使用之前要校驗下申請的地址是不是有效的

if (handle->vdmaVirtualAddress == MAP_FAILED) {
        perror("vdmaVirtualAddress mapping for absolute memory access failed.\n");
        return -1;
    }

然後還要申請影象存放的地址,這裡根據vdma的執行方式可以配置多個地址存放多幅影象資料。
下面是我的主程式,僅供參考:

int main() {

//variable start
    int j, i;
    Vec2b pix;
    struct timeval tstart, tend, hls_start, hls_end;
    float timeuse;
    Mat src_rgb = imread(INPUT_IMAGE, 1);
    Mat src_yuv(src_rgb.rows, src_rgb.cols, CV_8UC2);
    Mat dst_yuv(src_rgb.rows, src_rgb.cols, CV_8UC2);
    Mat dst_rgb(src_rgb.rows, src_rgb.cols, CV_8UC3);
    //convert to yuv format
    cvtcolor_rgb2yuv422(src_rgb, src_yuv);
    IplImage src = src_yuv;
    IplImage dst = dst_yuv;
//variable end

#if SW_GENERATE
    printf("opencv software processing\n");
    //calculate software used time 
    gettimeofday(&tstart, NULL);
    opencv_sobel_init();
    opencv_sobel(&src, &dst);
    gettimeofday(&tend, NULL);
    timeuse = 1000000 * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);
    timeuse /= 1000000;
    printf("soft used time  is %f\n", timeuse);
    cvtColor(dst_yuv, dst_rgb, CV_YUV2BGR_YUYV);
    imwrite(OUTPUT_IMAGE_GOLDEN, dst_rgb);
#endif

    if (!(init_filter() == XST_SUCCESS))
    {
        printf("filter init faild!");
    }
    set_reg_filter();

    // Setup VDMA handle and memory-mapped ranges
    vdma_setup(&handle, 0x43000000, 640, 480, 2, 0x1f400000, 0x1f800000, 0x1fc00000);

    gettimeofday(&tstart, NULL);
    memcpy(handle.fb1VirtualAddress, (uchar *)src.imageData, 640 * 480 * 2);

#if MEMCPY_CHECK
    printf("memcpy checking \n");
    u32 memcpy_error_flag = 0;
    for (i = 0; i < src_yuv.rows; i++) //row 480
    {
        for (j = 0; j < src_yuv.cols; j++) //col 640*2
        {
            pix = src_yuv.at<Vec2b>(i, j);
            if ((handle.fb1VirtualAddress[j * 2 + i * 640 * 2] != pix.val[0]) || (
                        handle.fb1VirtualAddress[j * 2 + i * 640 * 2 + 1] != pix.val[1]))
            {
                memcpy_error_flag = 1;
            }
        }
    }
    if (memcpy_error_flag == 1)
    {
        printf("img copy error");
        return 0;
    }
    //memset(handle.fb1VirtualAddress, 0, handle.width * handle.height * handle.pixelLength);
    printf("memcpy check result FB2:(ORI)\n");
    for (j = 512; j < 512 + 20; j++) printf(" %02x", handle.fb2VirtualAddress[j]); printf("\n");

#endif


    gettimeofday(&hls_start, NULL);
    vdma_start_triple_buffering(&handle);
    //printf("hahahaha\n");

    wait_done_filter();
    gettimeofday(&hls_end, NULL);

#if  RESULT_CHECK
    printf("RESULT CHECK FB2:(NOW)\n");
    for (j = 635 * 2; j < 635 * 2 + 20; j++) printf(" %02x", handle.fb2VirtualAddress[j]); printf("\n");
    //}
#endif
    memcpy((uchar *)dst.imageData, handle.fb2VirtualAddress, 640 * 480 * 2);

    gettimeofday(&tend, NULL);
    timeuse = 1000000 * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);
    timeuse /= 1000000;
    printf("hard total used time  is %f\n", timeuse);

    timeuse = 1000000 * (hls_end.tv_sec - hls_start.tv_sec) + (hls_end.tv_usec - hls_start.tv_usec);
    timeuse /= 1000000;
    printf("hard  hls  used time  is %f\n", timeuse);
    print_vdma_register_status();
    cvtColor(dst_yuv, dst_rgb, CV_YUV2BGR_YUYV);
    imwrite(OUTPUT_IMAGE, dst_rgb);
    stop_filter();
    // Halt VDMA and unmap memory ranges
    vdma_halt(&handle);
    return image_compare(OUTPUT_IMAGE, OUTPUT_IMAGE_GOLDEN);
}

vdma暫存器配置參考pg020_axi_vdma文件

軟體編譯選項

這裡單獨列出來是因為感覺在這種處理器效能不是很好的硬體平臺下進行大計算量演算法的實施的情況下,一定要讓軟體以最高效率執行(盡力最高效率吧)下面是辛辛苦苦寫的Makefile,用的萬用字元@梅神,稍微改改就可以用在新工程上

CC=g++
CFLAGS= -g -O2 -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad #-mfloat-abi=softfp -ffast-math
CFLAGS+=`pkg-config --cflags opencv`
LDFLAGS+=`pkg-config --libs opencv`

OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
OBJS += $(patsubst %.cpp,%.o,$(wildcard *.cpp))
all:  vdma_test

%.o: %.cpp
    $(CC) -c $(CFLAGS)  -o [email protected] $<
%.o: %.c
    $(CC) -c $(CFLAGS)  -o [email protected] $<
vdma_test: $(OBJS)
    $(CC) -o [email protected]   $(OBJS) $(LDFLAGS) 
clean:
    rm vdma_test $(OBJS)

cflag的優化配置參考的xapp1206-boost-sw-performance-zynq7soc-w-neon文件。

相關推薦

ZYNQ HLS影象處理加速總結()——Processing System軟體部分

概述 7010的硬核是兩個Cortex-A9,主頻666M(233333….),硬浮點+neon協處理器,效能不是很好,因為xilinx SDK可以生成底層IP的driver,所以PS裸跑起來很簡單,通過JTAG除錯很方便。初期時考慮到跑linux系統時的HL

數字影象處理——緒論總結

強度或灰度:一副影象可以定義為二維函式f(x,y),其中x和y是空間(平面)座標,而在任何一對空間座標處的幅值f即稱為強度或灰度。 數字影象:空間座標和灰度均不連續、用離散的數字表示的影象,能用計算機處理。畫素是其最小單位。 影象處理:對影象資訊進行加工滿足人視覺或應用需求的行為,包括影象變

Java伺服器部署基於OpenCV的C++影象處理專案()編譯篇

Java伺服器部署基於OpenCV的C++影象處理專案(二)編譯篇 部署好環境之後,本篇記錄編譯的詳細步驟。部署環境篇 1.建立檔案 在native.cpp中寫入以下程式碼。 #include <jni.h> #include <string>

python+OpenCV影象處理(十)車牌定位中對影象的形態學組合操作處理

車牌定位中對影象的形態學組合操作處理 所謂的車牌定位,其中最關鍵的部分就是對圖片的處理,引數的設定,並使之擁有泛化能力。 首先傳入圖片,在進行大規模的圖片處理時,因為無法確定圖片的尺寸,所以需要將原始圖片進行等比例的縮放。 orgimg = cv2.imread('ch

【數字影象處理】作業 邊緣檢測與追蹤

作業二 邊緣檢測與追蹤 吳政億 151220129 [email protected] (南京大學 電腦科學與技術系, 南京 210093) 邊緣檢測 在邊緣檢測中,我使用了sobel,roberts,prewitt,marr作為運算元,對

數字影象處理筆記():使用OpenCV處理影象

1 - 傅立葉變換 傅立葉變換是影象處理的基礎,Joseph Fourier(約瑟夫 ⋅ \cdot ⋅

Android端影象處理方法總結

Android端影象處理方法 在Android機中進行影象處理,常用的方式有兩種: 一種是單純使用JAVA語言進行圖形處理,相當於你將C或者C++編寫的影象處理方法,又重新用JAVA編寫了一遍。這種開發方法需要你在opencv官網,首先下載好Opencv的Android的版本,然後將它

影象處理】VTK影象標記bug修正

遊戲或者CAD軟體中,我們常常在目標旁邊顯示一個始終面向使用者的二維影象,來表示某些特定標識。VTK使用vtkFollower 來實現這個功能,然而VTK7.1及以下版本發生bug,表現為使用vtkFollower之後,二維影象無法渲染,只顯示一個純白色的矩形框

海康威視攝像頭+OpenCV+VS2017 影象處理小結(

第二篇 先介紹海康威視SDK的配置; 接著介紹並提取OpenCV庫的標頭檔案以及庫檔案供工程使用,期間還演示瞭如何配置屬性表; 最後將海康威視的SDK進行封裝,封裝成HK_camera類,並在主函式中生成並呼叫這個類的例項

數字影象處理複習總結

複習著感覺記不住,於是乎,有了這篇博文,如果也同樣選修了數字影象處理課程的小夥伴們可以參考一哈! 純手碼字…逢考必過! 概念 取樣與量化 灰度變換緩慢的景物:粗取樣、細量化 有大量細節變化的影象:細取樣、粗量化 取樣不夠出現馬賽克;量化不夠出現假輪廓

數字影象處理知識點總結

常用濾波器: 中值濾波器:它能濾除椒鹽噪聲、高斯噪聲,並且相對於均值濾波器來說,他不僅能過濾噪聲而且不會模糊邊界,保護了影象尖銳的邊緣。 均值濾波:平滑影象,速度較快,演算法簡單,只能很小程度的削弱噪聲,不能消除噪聲。缺點是會模糊影象細節。 高斯濾波器:對於抑制服從正態

影象處理(十影象融合(1)Seamless cloning泊松克隆-Siggraph 2004

Seamless cloning泊松克隆 作者:hjimce 本篇博文主要講解2004年Siggraph的經典paper:《Poisson Image Editing》,在影象融合領域,融合效果最牛逼的paper。講這個演算法,我沒打算講太多理論的公式,理論的東西,對於大

影象處理】彩色影象處理(Color Image Processing

實驗要求   (1.a) 編寫程式實現圖6.23,程式的輸入為影象中指定的兩個灰度級範圍。程式的輸出為RGB 格式影象,其中,一個灰度級範圍顯示為指定的彩色,其餘的畫素以RGB 形式顯示為與輸入影象對應畫素相同的灰度色。   (1.b) 用上述程式對圖1.1

影象處理Matlab()——生成隨機點

figure; set(gcf,'Position',[400,300,200,200]); a1 = 40; set(axes,'Color',[128/255 128/255 128/255]); hold on C = randi([-100,100],a1,1);   % 隨機生成整數型函式 C1 =

Win8 Metro(C#)數字影象處理--2.40影象輪廓提取演算法

 [函式名稱] 二值影象輪廓提取 ContourExtraction(WriteableBitmap src) [演算法說明]   二值影象的輪廓提取對於影象識別,影象分割有著重要意義。該演算法的核心就是將影象目標的內部點消除。所謂內部點,我們要根據當前畫素點的鄰域來進

【OpenCV3影象處理】查詢影象的邊緣 ( findContours()函式詳解 )

void cv::findContours ( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode

數字影象處理的筆記(一)第一部分:概念和應用領域

影象處理的部分先做一個大概的記錄,後續深入學習後展開。內容圍繞影象的分割和增強展開。 只是相關技術的某些應用,也可能不盡準確,為了記錄和學習。 第一部分:概念和應用領域 第二部分:相關的程式碼實現 ----------------------------針對畫素本身的操作,非

影象處理中的全域性優化技術(Global optimization techniques in image processing and computer vision) ()

原文:http://blog.csdn.net/mulinb/article/details/9079645 MulinB按:最近打算好好學習一下幾種影象處理和計算機視覺中常用的 global optimization (或 energy minimization) 方

Python影象處理庫PIL中影象格式轉換(

參考:https://blog.csdn.net/icamera0/article/details/50843196?utm_source=blogxgwz0 接上一篇《Python影象處理庫PIL中影象格式轉換(一)》 二、其他不同模式轉換為“RGB”模式 模式“RGB”為24位彩色影

影象處理影象識別筆記(影象變換

在本章節中,將介紹幾種常用的影象變換的方法,即利用數學公式將影象變換成另一種具有特定物理意義的影象,通過新的影象,我們可以觀察出原影象的某些特性,且可以對原影象進行濾波、壓縮等影象處理的操作,包括傅立葉變換、沃爾什變換等。 一、影象傅立葉變換基本原理與實現 傅立葉變換,是將時域訊號轉