본문 바로가기

HW

[Xilinx] hls::Mat 사용에 대해서 // HLS convolution tutorial 이해를 위해

[HW] - Xilinx HLS tutorial (1) - VIVADO install & Tutorial Data Download

[HW] - Xilinx HLS tutorial (2) - Creating HLS project

[HW] - Xilinx HLS tutorial (3) - Tcl Command Interface 사용하기

[HW] - Xilinx HLS tutorial (4) - Port 설정

 

 

이전 글들에서 HLS를 어떻게 사용하는지를 보고 있습니다. 

 

온라인을 돌아다니다가 아래와 같은링크를 보게 되었는데요 

 

https://medium.com/@chathura.abeyrathne.lk/xilinx-vivado-hls-beginners-tutorial-custom-ip-core-design-for-fpga-59876d5a4119

 

Xilinx Vivado HLS Beginners Tutorial : Custom IP Core Design for FPGA

My other articles :

medium.com

위 링크에 내용을 이해하기 위해서 필요한 간단한 내용들을 적어보고자 합니다. 

 

 

우선 해당 링크는 HLS를 통한 Convolution을 진행하는것을 예제로 하고 있습니다. 

 

HLS가 무엇인지는 읽으시는 분들께서 이미 알것이라 생각하지만

모르신다면 위에 있는 이전글 보기를 통해서 무엇인지 살펴보시기 바랍니다. 

 


처음으로 나오는 내요은

 

header file을 추가하는 부분인데요

 

두가지 해더파일을 추가합니다. 

#include <hls_video.h>
#include <stdint.h>

 

위 헤더파일에서 이해를 위해 살펴봐야 할것은

 

hls_video.h 입니다. 

 

해당 헤더 파일에 대한 내용은

Xilinx Confluence에 나와있는데요 

 

링크는 아래와 같습니다. 

 

 

https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841665/HLS+Video+Library

 

Confluence

 

xilinx-wiki.atlassian.net

 

 

링크에서 살펴보면

 

Video Library Data Strucutres라는 부분이 있는데 

 

hls_video.h에서 제공하는 Data Structures에 대해 나와있는 부분입니다. 

 

4가지 데이터 구조를 사용할 수 있는데 

 

hls::Mat<ROWS, COLS, T>
hls::Scalar<N, T>
hls::Window<ROWS, COLS, T>
hls::LineBuffer<ROWS, COLS, T>

 

위 4가지 입니다. 

 

본 예제에서 중요한것 두가지는

 

hls::Mat과 hls::Window 입니다. 

 


우선 바로 살펴보면

 

conv.cpp 로 저장할 hls로 합성될 ip의 C++ 코드는 아래와 같습니다

 

Convolution을 수행하는 코드인데 

 

 

#include <hls_video.h>
#include <stdint.h>
void conv(uint8_t image_in[1080*1920],uint8_t image_out[1080*1920]){

   const char coefficients[3][3] = { {-1,-2,-1},
                                     { 0, 0, 0},
                                     { 1, 2, 1} };
 
   hls::Mat<1080,1920,HLS_8UC1> src;
   hls::Mat<1080,1920,HLS_8UC1> dst;
   hls::AXIM2Mat<1920,uint8_t,1080,1920,HLS_8UC1>(image_in,src);
   hls::Window<3,3,char> kernel;
   for (int i=0;i<3;i++){
      for (int j=0;j<3;j++){
         kernel.val[i][j]=coefficients[i][j];
      }
   }
   hls::Point_<int> anchor = hls::Point_<int>(-1,-1);
   hls::Filter2D(src,dst,kernel,anchor);
   hls::Mat2AXIM<1920,uint8_t,1080,1920,HLS_8UC1>(dst,image_out);
}

 

간략하게 살펴보도록하겠습니다. 

 

일단 conv라는 함수가 입력으로 image_in 과  image_out을 받습니다.

 

return은 void이지만 분위기상 image_out으로 값이 나가겠구나 하시면 됩니다. 

 

const char coefficients를 통해서 filter값을 설정하고 

 

이어서 hls::Mat이 등장합니다. 

 

ROWS, COLS 크기로 값을 설정하고 어떤 type으로 할것인지도 설정합니다. 

 

그럼 직접 Convolution을 짜보고 싶은 분들은 아마

 

hls::Mat 이라고 적힌게 마치

cv::Mat과 같이 생겨서

 

at 연산자로 접근해야지

(참고 : https://blog.naver.com/doksg/221587551023)

 

OpenCV Mat 초기화 (C++)

OpenCV에서 Mat은 매우 중요한 클래스이다. 거의 모든 연산에 Mat을 사용하기 때문이다. 그런데 Mat...

blog.naver.com

라고 생각하실수도 있는데 

 

hls::Mat 연산자는 at을 통한 접근이 안됩니다. 

 

 

데이터를 읽기 위해서는

 

hls::AXIM2Mat<1920, uint8_t, 1080,1920,HLS_8UC1>(image_in, src);

 

위 함수를 이용해서 

 

함수의 입력으로 받아온 image_in과 

hls::Mat을 연결해 줘야합니다.

 

그리고 src.read() 함수를 사용하게 되면

image_in으로부터 hls::Mat선언시에 설정한

타입 크기(HLS_8UC1)만큼의 값을 읽어서 return해줍니다.

 

따라서 값에 대한 random access가 불가능합니다. 

 

그럼 값에 random access를 하고 싶다면 어떻게 해야될 까요?

 

정답은 Windows를 사용하는 것입니다. 

 

사용예시는 이어서 바로 나오는데 

 

 

위 예제코드에서는 kernel이 window로 선언되어 있고 

 

kernel.val[ i ][ j ] 를통해서 값에 접근하고 있는 모습을 보실 수 있습니다.

 


아마 hls::Mat과 hls::Window의 차이 때문에 짜증나는 분들이 있을것 같아서 적어봤는데 

 

사실 매우 쉽게 답을 찾을 수 있는 문제여서 큰 도움이 되셨을거 같진 않습니다만...

 

그래도 요즘 딥러닝 최적화 때문에 많이들 보시는것 같아 정리하여 올려드립니다. 

 


하나 더 생각해보면

 

딥러닝 최적화에서는 data reuse를 고려하는 것이 중요한데 

 

계속 사용되는 데이터를 저장해두지 않으면

데이터를 가져오기 위해서 반복적으로 메모리에 접근해야 하고 

해당 과정은 불필요한 전력낭비가 될수 있기 때문입니다. 

 

그렇다고 모든 데이터를 메모리에 담아두고 연산하기에는

속도가 느리거나 너무 큰 메모리가 요구되거나 하는

한계점이 존재합니다. 

 

적절한 절충점을 찾아서 설계를 진행하시면

멋진 가속기를 만드실 수 있을거라 생각합니다. 

 

감사합니다.