OpenCL やってみた + α みよし たけふみ わさらぼ 2016/08/24
DE0-nano-SoCでOpenCLAtlasのサンプル (Mandelbrot)からはじめるのがよい?
I buildrootな Linux環境I いくつかのユーティリティを追加
I dhcpcd,ssh,ImageMagick,lighthttpdI FPGAは/dev/acl0に見えているI OpenCL Mandelbrot Demo on Alteraを読む&試す@Qiita
OpenCLで I/O叩くI Alteraの I/Oチャネルが用意されてるI HDL側のレジスタにOpenCLで値を出力→ LEDへI データムーバができれば,後は何とかなりそう
OpenCLKernel
Register
FIFO
LED
CPUMain Memory
ドキュメントを参考に,
OpenCLコードとボード定義を作ってみる
I/Oチャネルで I/OするOpenCLのコード.読んだり書いたり.
channel u in t inCtr lCh __attribute__ (( i o ( ” aux_ctr l_in ” ) ) ) ;channel u in t outCtr lCh __attribute__ (( i o ( ” aux_ctr l_out ” ) ) ) ;channel u in t inDataCh __attribute__ (( i o ( ”aux_data_in” ) ) ) ;channel u in t outDataCh __attribute__ (( i o ( ”aux_data_out” ) ) ) ;
__kernel void ct r l_wr i te_read ( i n t a ,__global i n t * r e s t r i c t b ){
wr i te_channe l_a l te ra ( outCtrlCh , a ) ;*b = read_channel_altera ( inCtr lCh ) ;
}
__kernel void data_write_read ( __global i n t * r e s t r i c t a ,__global i n t * r e s t r i c t b ){
f o r ( i n t i = 0; i < 256; i++){wr i te_channe l_a l te ra (outDataCh , a [ i ] ) ;b [ i ] = read_channel_altera ( inDataCh ) ;
}}
カメラをつないでみよう
I 定番,OV7670をカメラに直結I SCCB経由でのレジスタ設定をOpenCL経由で
I 設定しないと動かないI 結構面倒.読んだり書いたり.I プリミティブ操作をカーネルで実装
I 1フレームのデータをキャプチャしてメインメモリにI YUV→ RGBの SW実装 v.s. HW実装で処理時間を比較
システム構成
ov7670_ctrl
ov7670_data
Register
FIFO ov7670
_capture
sccb_ifaceOV7670
OpenCL
CPUMain Memory
Cynclon V SoC
YUV422
YUV422 or RGB
Y0 U Y1 V
カメラ画像取得 - 取得するだけ
__kernelvoid ov7670_capture ( __global unsigned i n t * r e s t r i c t buf ){
const i n t s i z e = 320 * 480;f o r ( i n t i = 0; i < s i z e ; i++){
i n t d = read_channel_altera ( inDataCh ) ;buf [ i ] = d ;
}}
I Y0UY1Vの順でデータを書き出すI YUV->RGBはソフトウェアで変換
カメラ画像取得 - 取得しつつRGBに#def ine RANGE(x ) ( x < 16 ? 16 : x > 235 ? 235 : x )__kernel void ov7670_capture ( __global unsigned char * r e s t r i c t buf ){
const i n t s i z e = 320 * 480;f o r ( i n t i = 0; i < s i z e ; i++){
i n t d = read_channel_altera ( inDataCh ) ;i n t y0 = (d >> 24) & 0x000000FF ;i n t cb = (d >> 16) & 0x000000FF ;i n t y1 = (d >> 8) & 0x000000FF ;i n t cr = (d >> 0) & 0x000000FF ;i n t r0 , g0 , b0 ;r0 = y0+140750*(cr −128)/100000;g0 = y0− 34550*(cb−128)/100000−71690*(cr −128)/100000;b0 = y0+177900*(cb−128)/100000;i n t r1 , g1 , b1 ;. . .i n t pt r ;p t r = 3*(640*480−1−(2* i ) ) ;buf [ p t r+0] = ( unsigned char ) (RANGE(b0 ) ) ;buf [ p t r+1] = ( unsigned char ) (RANGE(g0 ) ) ;buf [ p t r+2] = ( unsigned char ) (RANGE( r0 ) ) ;pt r = 3*(640*480−1−(2* i +1));. . .
}}
動いたよ
I capture: 41.994msI conv: 14.888msI write bmp: 7.602msI total: 64.484ms
I capture: 36.725msI write bmp: 7.614msI total: 44.339ms
※ SW 側は,データ転送の工夫で性能改善の余地はある
コードの例
test/dot-product.kfc; ; Dense dot product between two vec to r s .
(module( de f i n e (main )
( l e t ( (X ( vector 1 2 3 4))(Y ( vector 4 3 2 1)))
( l e t ( ( dot ( reduce + ( ke rne l ( ( x X) ( y Y))(* x y ) ) ) ) )
( p r i n t l n dot )( as se r t (= dot 20))0 ) ) ) )
コンパイル
. / har lanc −g −v te s t /dot−product . k fc
出力されたカーネル (抜粋)
__kernel void harlan_main_282 ( reg ion_ptr danger_vector_259 , reg ion_ptr kern_256 ,i n t str ide_20 , reg ion_ptr y_32 , reg ion_ptr x_33 ,void __global * rk_143_285 , void __global * rv_53_284 , void __global * rv_63_283)
{reg ion __global * rk_143 = (( reg ion __global *)( rk_143_285 ) ) ;r eg ion __global * rv_53 = (( reg ion __global *)( rv_53_284 ) ) ;r eg ion __global * rv_63 = (( reg ion __global *)( rv_63_283 ) ) ;{
i n t __global * retva l_260 = (&((( i n t __global *)( get_region_ptr ( rk_143 , ( kern_256 ) + ( 8 ) ) ) ) [ get_global_id ( 0 ) ] ) ) ;
i n t i_34 = get_global_id ( 0 ) ;i n t x_36 = (( i n t __global *)( get_region_ptr ( rv_63 , (x_33) + ( 8 ) ) ) ) [ i_34 ] ;i n t y_35 = (( i n t __global *)( get_region_ptr ( rv_53 , (y_32) + ( 8 ) ) ) ) [ i_34 ] ;i n t t_37 = (x_36) * (y_35 ) ;i n t reduce_dindex_40 = ( i_34 ) + ( st r ide_20 ) ;i n t stepv_39 = str ide_20 ;i n t stopv_38 = *(( i n t __global *)( get_region_ptr ( rv_63 , x_33 ) ) ) ;whi le ( ( reduce_dindex_40 ) < ( stopv_38 ))
{i n t x_42 = (( i n t __global *)( get_region_ptr ( rv_63 , (x_33) + ( 8 ) ) ) ) [ reduce_dindex_40 ] ;i n t y_41 = (( i n t __global *)( get_region_ptr ( rv_53 , (y_32) + ( 8 ) ) ) ) [ reduce_dindex_40 ] ;t_37 = ( t_37) + (( x_42) * (y_41 ) ) ;reduce_dindex_40 = ( reduce_dindex_40 ) + ( stepv_39 ) ;
}* retva l_260 = t_37 ;
}}
出力されたホストコード (抜粋)
. . .c l : : k e rne l harlan_main_282_288 = g_prog . c rea teKerne l ( ”harlan_main_282” ) ;i n t dim_289 = str ide_20 ;unmap_region ( rk_143 ) ;unmap_region ( rv_53 ) ;unmap_region ( rv_63 ) ;harlan_main_282_288 . setArg (0 , danger_vector_259 ) ;harlan_main_282_288 . setArg (1 , kern_256 ) ;harlan_main_282_288 . setArg (2 , s t r ide_20 ) ;harlan_main_282_288 . setArg (3 , y_32 ) ;harlan_main_282_288 . setArg (4 , x_33 ) ;harlan_main_282_288 . setArg (5 , get_c l_buf fe r ( rk_143 ) ) ;harlan_main_282_288 . setArg (6 , get_c l_buf fe r ( rv_53 ) ) ;harlan_main_282_288 . setArg (7 , get_c l_buf fe r ( rv_63 ) ) ;g_queue . execute ( harlan_main_282_288 , dim_289 ) ;. . .i n t dot_48 = if_res_272 ;p r i n t ( dot_48 ) ;a s s e r t ( ( dot_48 ) == (20) ) ;. . .
コンパイルしてみると
いくつか手を入れないとだめ
I オフラインコンパイルに対応
I $記号が使えないI ← 簡単なスクリプトで変換する程度
I _も好ましくないI ホストコードのDEVICE_TYPEの修正I clCreateProgramWithBinaryに対応
I あとは Altera OpenCLツールでコンパイルI aoc でハードウェア側を合成I ホストコードは arm-linux-gnuebahf-を使う
まとめ
I 簡単な I/Oの追加をテスト → カメラの接続I “組み込み”にも嬉しいかもI 汎用の CPU/FPGA協調動作環境として
I メインメモリが簡単に使えるI ソフトウェア/CPUとの連携もI I/Oアクセス直結の処理は見通しよく書ける
I IOチャネルアクセスが一箇所は,ちょっと面倒I OpenCLでは書かないのが吉?I それ,Synthesijerで...?I 沢山チャネルはやして外でアービトレーション?
I より高級言語で Altera OpenCLを使うI できる (できた)I 記号の使い方に注意が必要I 最適化とかはこれから考えてみるI Irohaでよいのでは?