DMAリストを使ってミスアライメントしたデータを拾い集める

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

DMA転送ではあらかじめ最低でも16バイトに整列したデータを用意しておくのが鉄則です。 しかしそうは言ってもアライメントの整列していないデータをDMA転送でかき集めてくる事があるかもしれません。 ここでは一例としてfloatの配列から任意の要素をDMA転送する例を提示します。

ソース Media:dma-nuts-gather.tag.gz


画像:dma-nuts-gather.png

基本的には2つの手順からなります

  1. アライメントをそろえた領域にDMA転送する
  2. memcpyを使ってコピー

解説

複数のfloat要素の取得するのにDMAリストコマンドを使います。普通のDMA転送でもいいのですが、ここでは1回のDMA転送で処理するためにDMAリストコマンドを使っています。各要素はミスアライメントされているので直接DMA転送する事ができません。ここでは一回allocaでとった一時領域にDMA転送したあと、正規の領域にmemcpyしています。

ここではメインメモリ上のfloatの配列から適当に16個LSに転送し、得られた値を表示しました。

array[1], value = 1.000000
array[2], value = 2.000000
array[3], value = 3.000000
array[5], value = 5.000000
array[6], value = 6.000000
array[8], value = 8.000000
array[9], value = 9.000000
array[11], value = 11.000000
array[16], value = 16.000000
array[23], value = 23.000000
array[24], value = 24.000000
array[25], value = 25.000000
array[26], value = 26.000000
array[30], value = 30.000000
array[33], value = 33.000000
array[40], value = 40.000000

解説

配列の任意の位置の要素のリストを取得する関数として dma_nuts_gather 関数を作りました。nuts gatherは木の実拾いの意味です。

void dma_nuts_gather (void* target_buf, unsigned int ea, unsigned int* indexes, int size, int n)

第1引数は得られたデータを書き込むバッファ領域、第2引数は配列のメインストレージの実効アドレス、第3引数は取得したい要素の添え字の配列、第4引数は要素1つののサイズ、第5引数は取得する要素の個数です。

関数の中身を見ていきます。まずallocaでDMA転送に使う一時領域を確保しています。

	void* missaligned_buf = alloca (n*16+128);
	void* buf = (void*)(((int)missaligned_buf + 127) & ~127);

DMA転送に使う一時領域としてallocaで16バイト×要素数+128バイト確保しています。1要素のサイズとしてsizeバイトではなく16バイトをとっているのは、DMAリストの最低転送サイズが16バイトだからです。さらに先頭が128バイトに整列するようにずらします。最初に128バイト余計にとったのはこの為です。

	mfc_list_element_t list[n];
	for (int i = 0; i < n; i++)
	{
		list[i].notify = 0;
		list[i].eal = ea + size * indexes[i];
		list[i].size = size;
	}

DMAリストで使うリストを作ります。ごく普通のDMAリストです。notifyで要素の転送が完了するごとにイベントを立てられますがここでは使いません。

	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リストコマンドを発行し一時領域に格納します。

	char* p = (char*)target_buf;
	char* q = (char*)buf;

	for (int i = 0; i < n; i++)
	{
		memcpy (p, q + (size*indexes[i])%16, size);
		p += size;
		q += 16;
	}


一時領域から実際の領域に1要素づつmemcpyでコピーします。1要素につき16バイト確保したバッファーのうち実際に欲しいデータが入っている4バイトだけを狙ってコピーしています。



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

表示
個人用ツール
Open Source Projects