DMAリストを使ってギャザーとスキャターを行なう

出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう

SPEはDMAリストと呼ばれる機能を使って一度のDMA転送でメインメモリ上の複数の領域とデータをやりとりすることができます。このような機能を一般にデータのギャザーとスキャターと呼びます。

画像:gather-scatter.png

普通のDMA転送と同じように転送アドレスとサイズには制限があります。詳しくはCBE_Archtecture.pdfをご覧ください。

目次

DMAリストを使った画像ファイルのブロックコピー

ここではDMAリストを使った例として画像ファイルの一部分を別の場所にコピーするサンプルを紹介します。 入力画像はinput.ppm、出力画像はoutput.ppmです。

画像:dma-list-sample.png

(32,32)-(96,96)の画像(ちょうどペンギンが表示されている所)を(128,128)-(192,192)にコピーします。 SPEはこれを2回のDMAリストを使って行ないます。

Media:dma-list.tar.gz

コードの解説

DMAリストで使うdma_list_element構造体は spu_mfcio.h で定義されています。

サンプルで使用する定数とバッファの大きさを解説します。

const int WIDTH = 300;
const int HEIGHT = 225;
const int BUF_WIDTH = 64;
const int BUF_HEIGHT = 64;
int buf[BUF_HEIGHT][BUF_WIDTH][3] __attribute__((aligned(128)));
const int STRIDE = sizeof(buf[0][0]) * WIDTH;

画像ファイルの大きさが 300x225 で、LSに用意するコピー用のバッファの大きさを 64x64 とします。 バッファは1ピクセルあたり3色で12バイトです。ストライドはある1行と次の1行の間のバイト数です。 図で表すとこうなります。

画像:dma-list-ls-memory-map.png

コードの解説に移ります。

const int ea = arg[0];

画像ファイルが置かれているメインメモリのアドレスをeaとします。eaはPowerPC用語でEffective Addressの略です。

ギャザー

int x = 32;
int y = 32;
mfc_list_element list[BUF_HEIGHT];

for (int i = 0; i < BUF_HEIGHT; i++)
{
	list[i].notify = 0;
	list[i].reserved = 0;
	list[i].size = sizeof(buf[i]);
	list[i].eal =  ea + i*STRIDE + x*sizeof(buf[i][0]);
}

1ラインに1要素で合計16個の要素からなるリストを作ります。 notifyは要素の転送が終わったときにストール通知イベントを受け取ります。ここでは使いません。 sizeは要素の転送サイズです。ここでは1ライン分のバイト数を指定しています。 ealはメインメモリのアドレスの下位32ビットです。この値を飛び飛びに指定することでギャザーが行なえます。

	spu_mfcdma64 (&buf,
				  0,
				  (unsigned int)&list,
				  sizeof(list),
				  0,
				  MFC_GETL_CMD);
	spu_writech(MFC_WrTagMask, 1<<0);
	spu_mfcstat(MFC_TAG_UPDATE_ALL);

DMAリストでは必ずspu_mfcdma64を使います。 bufでLSのバッファのアドレスを指定しています。リスト要素の転送が終わるたびに自動でインクリメントされるので先頭アドレスを渡すだけで十分です。第2引数でメインメモリの上位32ビットを渡します。PPE側を32ビットでコンパイルしているので、ここでは0です。 第3と第4引数でリストとリストのサイズを渡します。 第5引数はタグで第6引数がDMAリストコマンドです。

DMAリストの転送完了は普通のDMA転送と同じ手順で待ちます。 これでメインメモリからLSのバッファにコピーしました。

スキャター

次に座標を変えてメインメモリに書き戻します。

	x = 128;
	y = 128;
	for (int i = 0; i < BUF_HEIGHT; i++)
	{
		list[i].notify = 0;
		list[i].reserved = 0;
		list[i].size = sizeof(buf[i]);
		list[i].eal =  ea + (y+i)*STRIDE + x*sizeof(buf[i][0]);
	}

座標(x,y)が違うだけで残りはさきほどと同じです。

	spu_mfcdma64 (&buf,
				  0,
				  (unsigned int)&list,
				  sizeof(list),
				  0,
				  MFC_PUTL_CMD);
	spu_writech(MFC_WrTagMask, 1<<0);
	spu_mfcstat(MFC_TAG_UPDATE_ALL);

メインメモリに書き戻して完了です。



Cellプログラミングのレシピに戻る

表示
個人用ツール
Open Source Projects