Alveoとの仮想メモリ転送
メモリ割り当ての概念
・各ページのサイズは4KiBで、物理メモリの対応するページにマップされる。
・ポインタは通常仮想メモリアドレスを指定している。
・物理メモリでページが連続していない場合は、次のようにデータがAlveoにコピーされる。
(Alveo開発の基礎 p.10)
このような場合、データが大きいほどページテーブルを管理するための時間が多くかかる。
物理メモリでデータが連続していれば、管理はより簡単になるし、多くのDMA操作で有益となる。次のようなすっきりとした図になる。
(Alveo開発の基礎 p.11)
SGDMAはスキャッターギャザーDMAというらしい。
DMAはCPUを介さずにデータを転送する方式。CPUを使うのはPIO方式。
知りたいことを学ぶのが楽しめるようになった気がする
黄昏流星群を読んでたらこんなおじさん数学教師がいた。
『黄昏流星群』より
高校の教師は飯食うためにやってるだけで、それ以外の時間は数学の未解決問題を解くことに使っている。
この人にとってはそれが何よりも楽しいと感じるらしい。
自分から見たらそれは勉強で、勉強しててそんなに楽しいって羨ましいなあと、
ちょっと前の自分なら思ってた。
勉強=努力?楽しい?
でも最近になって、コンピュータの勉強をするのが面白く感じられるようになってきた。
もっと詳しくなりたいと思う欲求があって、朝からパソコンとにらめっこする。
その時間が楽しい。
前では考えられなかった。勉強=努力するもの、という意識ばかりだった。
その「努力」っていうのはすなわち我慢のことで、辛いけど勉強しなければならないんだ、と小学生の頃からずっと思ってた。
まあ学校の勉強はそうかもしれないけど、大学生になってもその思考の癖は変わらず。
だけども、本当に最近だけど、勉強することが楽しくなった。どんどんのめり込んでいってる。
なんでそう変わったかって言うと、もともと今研究してることに好奇心はあったから、っていう前提はあるんだけど、
『知りたいことを勉強していて「つまらない・つらい」と感じることもある。』ってことを受け入れられたからだ。
勉強を楽しいと感じたことはあまりなかった
前は、もし自分がしたい勉強をしているとしたら苦しいことなんか全然なくて、楽しいって感じるばかりで、
よく聞く「努力を努力と感じない」状態になるんだろうなーって思ってた。
だから、私が本当は学びたいことを学んでいても、ちょっと理解できなかったりして嫌な気持ちになると「本当に勉強したいわけじゃないのかな」という考えになった。
でも、そういう気持ちは素直に受け入れるというか、”あるがまま”にして、取り除こうとかせずに知りたいことを学び続ければいいんだ。って気づいた。
そしたら、理解できなかったときが「悔しいな、でも理解したいな」に変わった。
で、理解できたらちょっぴり嬉しい。
次はこれが知りたい。その繰り返しだ。
負の感情を避けようとしないこと
あとは、ちゃんと集中できるかどうかが大事かな。
「知りたいから、真剣にやるぞ」って気持ちだと楽しい。没頭できる。
「理解できなかったら苦しいし嫌だな、それは避けたいな」って気持ちで逃げ腰だと、集中できない。
嫌だな、って思っててもいいし、実際そういうときもあるけど、
避けようとか思わず、嫌なものは嫌なもので認めて、嫌がる。
好きなことをやっててもそういうときってあるよね、って考えて、また進む。
これって人によってはわざわざ考えなくてもいいことかもしれないけど、自分は言葉にして意識したら少し変われた。
もしかしたら同じ人がいるかな?って思う。
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に格納される。
Zynq UltraScale+ MPSoCの内部メモリと外部メモリについて概要
BRAM・URAMと外部DDRメモリの違いなどが知りたい。(参考サイト)
Xilinx の FPGA 内部のメモリ階層は(中略)、分散 RAM (Distributed RAM), BRAM (Block RAM), URAM (Ultra RAM) の 3種は FPGA 内に実装されています。
この”FPGA内”というのは次の写真の①のことかな。SoCのこと。
3つをざっくり説明すると...
【内部メモリ(RAM)】
・分散RAM:LUT を使用し、係数の格納、ステート マシン、および小規模バッファーとして有効。
・BRAM:高速かつ柔軟なデータ ストレージおよびバッファリングとして有効。
・URAM:各ブロック 288Kb のストレージを提供し、またブロックを接続して大規模なオンチップ ストレージを構築できます。(引用元)
内部メモリと比較して、外部メモリは次のようになっている(Zynq UltraScale+ MPSoCの場合)。
【外部メモリインタフェース】
Zynq® UltraScale+™ MPSoC には、プロセッシング システム内にハード メモリ コントローラーが含まれています。これらのデバイスは、32GB のアドレス可能なメモリをサポートできます。その他のメモリは、プログラマブル ロジック領域に追加できます。
上の表にある全てが搭載されるんじゃなくて、デバイスごとに搭載されるメモリは違うよね。
次に示すFigure3-1はMPSoCのブロックダイアグラムだけど、赤い円で囲った部分が「プログラマブルロジック メモリソリューション」のことかな。
(PSメモリソリューションはFigure2-1の②)
Programmable Logic の中にはBRAMやURAMなどの「内部メモリ」がある、という認識でいいだろうか。
もっと詳しいことは...ZCU102の場合UG1182に書いてあるからいつか見よう。今日は概要だけつかみたい。
次に、研究している自己組織化マップのハードウェアについてVitisでSystem DIagramを見てみる。
URAMはN/Aとなっているが、ビルド対象としたZCU102はURAMが使えない?
調べてみたら、使えるみたい。じゃあ単に利用してないのか。
↓小さいので抜き出してみた
UltraRAMは36Mb使えるみたい。
BRAMは最大で36Mbとなっていて、1つ36Kbだから865個で31140Kb。大体94.85%に近い値かな。
自分の書いたソースコードのうち、どの記述がBRAMを消費しているのか把握しよう。多分、カーネルで定義した配列はBRAM領域(=内部メモリ)を使うっぽい?
次に知りたいこと・疑問
・難しそうだけど、FPGAの内部メモリ(カーネル)とグローバルメモリ(ホストとカーネル共用のメモリ)のアクセス方法について知りたい。
・enqueueMigrateMemObjectsのおさらい
UG573を読んでXilinx UltraScaleのBRAMの概要を知りたい
UltaraScale アーキテクチャ メモリリソースユーザーガイド(UG573)
を読んで、いまだによく分かっていないBRAMを学ぶ。
(間違ってること書いてたらスミマセン。)
ブロックRAMの概要
UltraScale アーキテクチャデバイスのブロックRAMは2つの独立した18Kb RAM または 1 つの 36Kb RAM として構成可能で、いずれも最大 36キロビットのデータを格納できます。
1個のBRAMが36Kbのメモリと考えてよいのかな。
図1-1はBRAMをTDP(True Dual Port)モードで使用した場合の図で、18Kbの領域が2つあると考えるらしい。
それぞれの領域にアクセスできるポートがあって、読み出し・書き込みについて完全に独立している。クロックも別のものを利用できる。
各ブロックRAMには書き込みポートと読み出しポートが 2つずつあります。36KbブロックRAMをTDP (True Dual Port) メモリとして使用する場合、ポートの幅は個別に 32Kx1、 16Kx2、 8Kx4、 4Kx9、 2Kx18、 1Kx36 のいずれかに設定でき ます。
これって、例えば8Kx4っていうのは4ビットx8Kのポート幅という意味なんだろうか?
36KbブロックRAMをSDP(SimpleDualPort)メモリとして使用し、書き込みポートと読み出しポートをそれぞれ1つずつしか使用しない場合は、512x72ビットのポート幅も可能です。
なるほど。
72ビットって、1度に読み出せるのかな?そうすると、データバス幅が72ということ?それは可能なのか?よく分からない。
隣接するブロックRAMをカスケード接続し、下段のブロックRAMのデータ出力を上段のブロックRAMに入力することで、大容量のブロックRAMブロックを構成可能です。
これで32Kb以上のメモリ領域を使えるということかな。
BRAMの数はFPGAデバイスごとに異なるから、全体で使えるBRAMの記憶領域も違ってくる。
あと、FIFOとして使うことができるらしい。
ブロックRAMの説明
ブロックRAMは、デバイス全体でクロック領域(CR)内にカラム状に配置されます。
このCRというのは、PL領域にあるという認識でいいのかな?
ちなみにちょっと脱線するけどUG1182を眺めてたらXCZU9EG MPSoCのブロックダイアグラムがあったから載せておく。
下に載せたFigure 2-1の①が上のBlock Diagram。
今更だけどSoCの中身とか全然分かってなかった。上はZCU102の例。Figure3-1にCortex-A53ってのが4つあって、すなわちこれがクアッドコアARM。
これから少しずつ詳しくなっていきたいな。
あと下の図がXilinxのMPSoC。これがFigure2-1の①だ。こうやって比較して見ると分かりやすい。
話をBRAMに戻そう。
なんだか分かったようでまだ全然分かってない。
そもそもどこにあるのか?どんなときに使うのか?よく分からなくなってきた。一旦、先生に聞いてみよう...。
最後にブロックRAMとUltraRAMの比較表を載せておく。UltraRAMっていうのは、まだよく分かってないけどBRAMに似たものらしい。違いは表2-1にある。
まだまだ分からないことが膨大にあるけど少しずつ詳しくなっていきたいなあ。
今日はこの辺でやめとこう。
次に知りたいこと・疑問
・BRAMってPL部にある、という認識でよいのか?
・UltraRAMについて
・BRAMとUltraRAMを使うには?グローバルメモリとは?