clEnqueueMigrateMemObjectsとclEnqueueMapBufferの概要
まず、OpenCLのメモリオブジェクトについておさらい。
メモリオブジェクトは、バッファオブジェクトと画像オブジェクトの2つのタイプに分類される。
バッファオブジェクトが1次元配列の要素を格納し、画像オブジェクトが2次元または3次元のテクスチャ、フレームバッファ、または画像を格納する。
(OpenCL 詳説より)
メモリオブジェクトは、cl_memオブジェクトで取り扱われます。
バッファオブジェクトの場合、要素は連続した形で格納され、デバイス上で実行しているカーネルからポインタを用いてアクセスできる。
clCreateBuffer関数を用いて生成する。
clEnqueueMapBuffer
この関数で、指定したバッファにホストからアクセスできる(ように見える?)ポインタを取得できる。
cl_memオブジェクトには、ホスト側ポインタとデバイス側ポインタの2種類があり、clEnqueueMapBufferを使えば、デバイスのアドレス空間にあるグローバルメモリ上の特定のバッファを、ホストのアドレス空間にマッピングしてポインタ(ホスト側ポインタ)を返してくれる。
clEnqueueMigrateMemObjects
ホストからデバイスにデータを転送したいとき、
clEnqueueMapBufferで取得したホスト側のポインタにそのデータを書き込んで、clEnqueueMigrateMemObjectsにそのポインタを引数として渡すと、データをデバイスに転送できます。
ホストのグローバルメモリ領域に書いたデータをデバイスのグローバルメモリ領域に移してくれる、というイメージなのかな。
グローバルメモリをおさらい...
これはZCU102評価ボードのPlatform DiagramをVitisで表示したものだけど、
LPDとかHP0とか書いてあるメモリのイラストが「グローバルメモリ」で、ZYNQ(ホスト)とZU9EG(デバイス)の両方からアクセスできる。
デフォルトでは、カーネルがリンクされる場合、すべてのカーネルからのメモリ インターフェイスが 1 つのデフォルトのグローバル メモリ バンクに接続されます。(UG1188)
複数のメモリバンクを使うことも出来るけど、何も指定しなければ1つ。例えば次の図みたいな感じ。
要点
・ホストとデバイスの両方からアクセスできる「グローバルメモリ」は、それぞれに専有のアドレス空間がある。
・グローバルメモリにデバイスからアクセスしたいバッファを作った場合、そこには直接ホストからアクセスできない。なぜなら、バッファはデバイスのアドレス空間に置かれているから。
clEnqueueMapBuffer関数を使ってそのメモリオブジェクトの領域をホスト側のアドレス空間にマッピングすれば、ホストはその領域に対して読み書きできるようになる。アクセスが終わったらアンマッピングする。
次に知りたいこと・疑問
・カーネルはどのタイミングでグローバルメモリ上のデータをローカルメモリ(FPGAの内部メモリ BRAMなど)に転送するのか?
ハードウェア関数を呼び出したとき(カーネルを実行したとき)?
ハードウェア関数内で、引数にかかれた配列の要素を使う(読み出す)とき?
→おそらく後者。ハードウェア関数内で配列を定義して、そこにホストから転送されたデータを格納するときにグローバルメモリにアクセスする。その配列はBRAMに格納される。