l'essentiel est invisible pour les yeux

Monday, December 04, 2006

[Cell] CESOFを使用してPPU/SPU間で変数を相互に参照する

CESOF(Cell Embeded Object Format)は、実行可能SPEプログラムをPPEプログラムに埋め込むためのフォーマット。

前回の[Cell] PPUプログラムにSPUプログラムを組み込むでは、ppu-embedespuコマンドでCESOFオブジェクト(hello_spu-embed.o)を作成して静的ライブラリにしPPUプログラムのリンク時に合わせてリンクすることで、PPUプログラムからspu_program_handle_t型の外部変数としてSPUプログラムを参照可能になるようにした。

作成されたCEOSFのシンボル表には、hello_spuシンボルが定義されておりこれによりPPUプログラムでSPUプログラムを参照できるようになっている。


% nm spu/hello_spu-embed.o
00000000 D hello_spu
%

今回作成するプログラムについて
同じくCESOFを使用してPPU/SPU間で変数を相互に参照するコードを書く。PPU側で宣言された変数をSPU側で参照し出力する。その変数をSPU側書き換えてSPUの処理が完了するのを待ってからPPU側で出力する。

まずは、作業ディレクトリを作成し、その中に次のようにディレクトリとファイルを構成する。

% ls -R
.:
Makefile ppu/ spu/

./ppu:
Makefile ppu_hello.c

./spu:
Makefile spu_hello.c spu_hello_toe.s
%

ppu/ppu_hello.c
PPUプログラムではhello_stringというシンボル名を持つ変数を宣言する。この変数がSPU側から参照されることになる。

#include <stdio.h>
#include <stdlib.h>
#include <libspe.h>
#include <errno.h>
#include <sched.h>

#define BUF_SIZE 256

/* this symbol definded in spu_hello-embed.o */
extern spe_program_handle_t spu_hello;

char hello_string[BUF_SIZE] = "Hello, World!";

int main() {
int status;
speid_t spe_id;
spe_gid_t gid;

/* Create SPE thread group */
gid = spe_create_group(SCHED_OTHER, 0, 1);
if(gid == 0) {
fprintf(stderr, "Failed spe_create_group(errno=%d)\n", errno);
return 1;
}

/* Create SPE thread */
printf("hello_string: %p\n", &hello_string);
spe_id = spe_create_thread(gid, &spu_hello, NULL, NULL, -1, 0);
if(spe_id == 0) {
fprintf(stderr, "Failed spe_create_thread(errno=%d)\n", errno);
return 1;
}

/* Waiting SPE thread */
spe_wait(spe_id, &status, 0);

/* print hello_string */
printf("PPU: %s\n", hello_string);

return 0;
}


spu/spu_helllo.c
PPUで宣言したhello_stringをSPU側で参照し値を出力する。SPU側では、_EAR_というプレフィックスを付けることで、spuembedがCESOFオブジェクトを作成する際にシンボル名から_EAR_取り除いた値に変換してくれる。EARはEffective Address Referenceの略。
(シンボルについては、下記で説明)

#include <stdio.h>
#include <stdlib.h>
#include <libmisc.h> /* copy_to_ls and copy_from_ls */
#include <spu_mfcio.h>
#include <string.h>

static const int BUF_SIZE = 256;

/* to be resolved by CESOF in the system memory */
extern unsigned long long _EAR_hello_string;

int main(unsigned long long spuid __attribute__((unused)),
unsigned
long long argp __attribute__((unused))) {
char buf[BUF_SIZE];

/* Copy data from system memory to Local Storage */
printf("_EAR_hello_string: 0x%llX\n", _EAR_hello_string);
copy_to_ls((uint32_t)(&buf), _EAR_hello_string, sizeof(buf));
printf("SPU: %s\n", buf);

/* change buf */
strncpy(buf, "Welcome to SP program.", sizeof(buf));
copy_from_ls(_EAR_hello_string, (uint32_t)(&buf), sizeof(buf));

return 0;
}
LSとメインメモリ間のデータ転送については、copy_to_ls/copy_from_ls関数を使用する。それぞれ、libmisc.hで定義されている。ppu-embededにより作成されるCESOFオブジェクト(下記の例ではspu_hello-embbed.o)では、_EAR_がとりのぞかれhello_stringで参照するように変換され、PPEリンカがシンボル名を解決する。

% nm spu/spu_hello-embed.o
U hello_string
00000000 D spu_hello
% nm spu/spu_hello.o
00000000 r BUF_SIZE
U _EAR_hello_string
U copy_from_ls
U copy_to_ls
00000000 T main
U printf
U strncpy
%

このままでは、リンク時に_EAR_hello_stringシンボルの実効アドレスをリンカが解決できない。CESOFではEARは.toe(table of EARs)と呼ばれるセクションに置かれる。そこで、このセクションに関するアセンブリを書いて合わせてコンパイルする必要がある。(CESOFの詳細参照 <- 書いたらリンクに置き換える)

spu_hello_toe.s

.section .toe, "a", @progbits
.align 4
.global _EAR_hello_string
_EAR_hello_string:
.octa 0x0


作成したアセンブリはspu-asを使用することでオブジェクトファイルにコンパイルできる。コンパイルされたコードをリンク時に合わせてリンクすることで.toeセクションを追加できる。

% spu-as -I. -I /opt/IBM/cell-sdk-1.1/sysroot/usr/spu/include -o spu_hello_toe.o spu_hello_toe.s


CESOFの作成、静的ライブラリの作成、アセンブリコードのコンパイルといったこれらの処理も、下記に書くCell SDKが提供するMakefileを利用すれば全て自動的にやってくれる。

コンパイルに必要なMakefile達

Makefile
DIRS = spu ppu
include /opt/IBM/cell-sdk-1.1/make.footer
ppu/Makefile
PROGRAM_ppu = ppu_hello
IMPORTS = ../spu/spu_hello.a -lspe
include /opt/IBM/cell-sdk-1.1/make.footer
spu/Makefile
PROGRAM_spu   = spu_hello
LIBRARY_embed = spu_hello.a
IMPORTS = $(SDKLIB_spu)/libc.a $(SDKLIB_spu)/libmisc.a
include /opt/IBM/cell-sdk-1.1/make.footer


実行
# ./ppu_hello
hello_strint: 0x181688
_EAR_hello_string: 0x1816188
SPU: Hello, World!
PPU: Welocome to SPU Program.
#


TOEセクション

% spu-readelf -S spu/spu_hello | grep -w .toe
[ 6] .toe PROGBITS 00001680 0016c0 000010 00 A 0 0 16
%
% spu-objdump -s --start-address=0x1680 --stop-address=0x1690 spu/spu_hello

spu/spu_hello: file format elf32-spu

Contents of section .text:
Contents of section .rodata:
Contents of section .ctors:
Contents of section .dtors:
Contents of section .data:
Contents of section .toe:
1680 00000000 00000000 00000000 00000000 ................
Contents of section .comment:
Contents of section .debug_aranges:
Contents of section .debug_pubnames:
Contents of section .debug_info:
Contents of section .debug_abbrev:
Contents of section .debug_line:
Contents of section .debug_frame:
Contents of section .debug_str:
Contents of section .debug_loc:
Contents of section .note.spu_name:
%

TOEセクションの詳細とCESOFの動作原理の詳細については、あとで書く。