アトラスVMEアクセスフレームワークの提案
アトラスTGCエレキのスライステストに利用する計算機は
1台に留まらず、計算機を超えたVMEアクセスが求められます。
この状況の下で、ネットワークを介してのVMEアクセス、
CCI/HSCを介してのVMEアクセス、VMEインターフェースを
介してのVMEアクセスが全く異なったプログラムになって
しまってはプログラム開発の効率の点で問題があり、
多くのバグを再生産することにもなりかねなません。
そこで、これらのVMEアクセスを一様に扱えるフレームワークを
提案します。
このVMEアクセスフレームワークはローカルでのVMEアクセス及びネットワークを利用した
VMEアクセスに関して、一様なアクセス法を提供することによって、プログラムの大幅な
変更をしなくても、シームレスにプログラムの利用が可能となるように設計されました。
使用言語はC、C++及びJavaです。C++及びJavaについてはそれぞれCORBA及びHORBという
分散オブジェクト言語を用いたVMEアクセスが提供されていますので、ネットワークを
介したVMEアクセスが可能になっています。しかし、Cについては現在のところ、それは
用意されてはいません。
現在サポートされているVMEアダプタはBit3ですが、Universeチップや他の
アダプタについても導入可能な構造にしています。また、アトラスグループで
利用されるCCI/HSCのために特別なプログラムコードが実装されています。
一様なアクセス法とは言っても、全く同じコーリングシーケンスになっている訳では
ありません。言語の違いによって、実装法が違ってしまいます。
できるだけ同じように心がけました。手続き型言語のCとオブジェクト指向が可能な
C++/Javaの実装法の違いにより、引数の数が異なっています。
open
- driver
"vmehb" -- NataliaさんのVMEHBドライバ
"ccihsc" -- VMEHBドライバ経由のCCI/HSC
"universe" -- HannappelさんのんUniverseドライバ(実装されていない)
- space
spaceは次のようなパラメータをサポートする。
A32 -- VME A32 空間
A24 -- VME A24 空間
A16 -- VME A16 空間
DATA -- VME Dataアクセス
PRGM -- VME Program アクセス
PRV -- VME 特権アクセス
NONPRV--VME 非特権アクセス
BLT -- VME ブロックDMAアクセス(32-bit)
MBLT -- VME ブロックDMAアクセス(64-bit)
NBLT -- VME 非ブロックDMAアクセス
下記のようなコードが用意されています。
A32PRVBLT
A32PRVPRGM
A32PRVDATA
A32PRVMBLT
A32NONPRVBLT
A32NONPRVPRGM
A32NONPRVDATA
A32NONPRVMBLT
A24PRVBLT
A24PRVPRGM
A24PRVDATA
A24PRVMBLT
A24NONPRVBLT
A24NONPRVPRGM
A24NONPRVDATA
A24NONPRVMBLT
A16PRVDATA
A16NONPRVDATA
D32 -- 32-bit アクセス(LWORD信号を使う)
D16 -- 16-bit アクセス
- vmeaddr
アクセスすべきモジュールのVMEアドレスを示す。
- size
アクセスする範囲をバイト単位で示す。
- RETURN VALUE
openVMEは成功時にVME IDを、エラー時に負の値を返す。
VME IDはvmedevicebox構造体からなるデータベースに
アクセスするためのIDで、その後のアクセスに必要とされる。
- ERRORS
- int vme_open( char *driver, int space );
- void vme_close( int vd );
- int vme_readInt( int vd, int vmeaddr, int *data);
- int vme_readShort( int vd, int vmeaddr, short *data );
- int vme_writeInt( int vd, int vmeaddr, int data );
- int vme_writeShort( int vd, int vmeaddr, short data );
- Non CORBA-based calling sequence
- VME_impl *vme = new VME_impl;
- int vme->open(const char* driver, int space);
- void vme->close(int vd);
- Result vme->readInt(int vd, int vmeaddr);
- Result vme_readShort(int vd, int vmeaddr);
- Result vme->writeInt(int vd, int vmeaddr, int data);
- Result vme->writeShort(int vd, int vmeaddr, short data);
- CORBA-based Calling Sequence
- Server_var vme;
- CORBA::Long vme->open(CORBA::String_var driver, CORBA::Long space space );
- void vme->close( CORBA::Long vd );
- Result vme->readInt( CORBA::Long vd, CORBA::Long vmeaddr );
- Result vme_readShort( CORBA::Long vd, CORBA::Long vmeaddr );
- Result vme->writeInt( CORBA::Long vd, CORBA::Long vmeaddr, CORBA::Long data );
- Result vme->writeShort( CORBA::Long vd, CORBA::Long vmeaddr, CORBA::Short data );
- HORB-based Calling Sequence
- int vme.open(String driver, int space );
- void vme.close( int vd );
- Result vme.readInt( int vd, int vmeaddr );
- Result vme.readShort( int vd, int vmeaddr );
- Result vme.writeInt( int vd, int vmeaddr , int data );
- Result vme.writeShort( int vd, int vmeaddr, short data );
これはVMEをCプログラムからアクセスする方法を提供します。
// C
#include <stdlib.h>
#include "vmelib.h"
#include "clientvmelib.h"
int user( void ) {
int vd;
int vmeaddress = 0x11000020;
int data;
int status;
vd = vme_open("vmehb", A32PRVDATA|D32);
printf("open : vme id = %d\n", vd );
data = 0x55555555;
status = vme_writeInt( vd, vmeaddress, data );
if( status )
printf("vme_writeInt: error occurred...\n");
printf("writeInt : written data = %d\n", data );
data = 0;
status = vme_readInt( vd, vmeaddress, &data);
if( status )
printf("vme_readInt: error occurred...\n");
printf("readInt : read data = %d\n", data );
vme_close( vd );
printf("close : done\n");
return 0;
}
int main(int argc, char* argv[]) {
int status = user();
}
これはC++言語を使用し、CORBAを使わないVMEアクセスの例です。
vmeというオブジェクトの関数であるopen/writeInt/readInt/closeを
呼び出します。open時にはドライバ名とアクセスする空間を指定します。
openはvme descriptionを返して来ます。それをキーに以下の
writeInt/readInt/closeを呼び出します。
// C++
#include <sys/time.h>
#include <unistd.h>
#include <fstream.h>
#include <stdlib.h>
#include "commonvmelib.h"
#include "vme_impl.h"
int e_time(struct timeval *first, struct timeval *second) {
if (first->tv_usec > second->tv_usec) {
second->tv_usec += 1000000;
second->tv_sec--;
}
return( (int)(second->tv_sec - first->tv_sec)*1000000
+ (int)(second->tv_usec - first->tv_usec));
}
int main(int argc, char* argv[]) {
char driver[10] = "vmehb";
int space = A32PRVDATA|D32;
int vmeaddress = 0x11000000;
int data;
VME_impl *vme = new VME_impl;
Result result;
int i;
struct timeval start, stop;
struct timezone tzpstart, tzpstop;
int vd = vme->open(driver, space);
cout <<"open : vme id = " << vd << endl;
data = 12345678;
result = vme->writeInt( vd, vmeaddress, data );
cout <<"writeInt : written data = "<< data << endl;
result.data = 0;
result = vme->readInt( vd, vmeaddress);
cout <<"readInt : read data = "<< result.data << endl;
gettimeofday (&start, &tzpstart);
for(i = 0; i < 1000; i++)
result = vme->writeInt( vd, vmeaddress, data );
gettimeofday (&stop, &tzpstop);
cout << "elapsed time of vme->writeInt = " << e_time( &start, &stop)/1000 <<
" in microsec." << endl;
gettimeofday (&start, &tzpstart);
for(i = 0; i < 1000; i++)
result = vme->readInt( vd, vmeaddress);
gettimeofday (&stop, &tzpstop);
cout << "elapsed time of vme->readInt = " << e_time( &start, &stop)/1000 <<
" in microsec." << endl;
vme->close( vd );
cout <<"close : done" << endl;
}
これはCORBAを使ったVMEアクセスの例です。ユーザはuserという
ルーチンを変更して自分のコードを埋めてください。
他の変更は不要です。この例題(userのみ)を手順を追って説明します。
vmeというオブジェクトの関数であるopen/writeInt/readInt/closeを
呼び出します。open時にはドライバ名とアクセスする空間を指定します。
openはvme descriptionを返して来ます。それをキーに以下の
writeInt/readInt/closeを呼び出します。
// C++
#include <OB/CORBA.h>
#include <server.h>
#include <fstream.h>
#include <stdlib.h>
#include "commonvmelib.h"
int user(Server_var vme) {
CORBA::String_var driver = CORBA::string_dup("vmehb");
CORBA::Long space = A32PRVDATA|D32;
CORBA::Long vmeaddress = 0x11000000;
CORBA::Long data;
Result result;
CORBA::Long vd = vme->open(driver, space);
cout <<"open : vme id = " << vd << endl;
data = 12345678;
result = vme->writeInt( vd, vmeaddress, data );
cout << "writeInt : written data = " << data << endl;
result.data = 0;
result = vme->readInt( vd, vmeaddress);
cout << "readInt : read data = " << result.data << endl;
vme->close( vd );
cout << "close : done" << endl;
return 0;
}
int run(CORBA::ORB_ptr orb) {
const char* refFile = "../../ior_files/server.ref";
ifstream in(refFile);
CORBA::String_var s;
char *strret;
in >> s;
CORBA::Object_var obj = orb -> string_to_object(s);
if(CORBA::is_nil(obj)) {
cerr << "cannot read IOR from Hello.ref" << endl;
return EXIT_FAILURE;
}
Server_var vme = Server::_narrow(obj);
assert(!CORBA::is_nil(vme));
int status = user(vme);
return status;
}
int main(int argc, char* argv[]) {
int status = EXIT_SUCCESS;
CORBA::ORB_var orb;
try {
orb = CORBA::ORB_init(argc, argv);
status = run(orb);
} catch(const CORBA::Exception&) {
status = EXIT_FAILURE;
}
if(!CORBA::is_nil(orb)) {
try {
orb -> destroy();
} catch(const CORBA::Exception&) {
status = EXIT_FAILURE;
}
}
return status;
}
これはJava/HORBを使った例題です。ユーザは
userルーチンのみを変更して使います。
上記のC++/CORBAの例と同じように、まずはvmeオブジェクトを
受けて、そのメソッドであるopen/writeInt/readInt/closeを
呼びます。
import horb.orb.*;
class Client {
static int user( Server_Proxy vme ) {
int vme_addr = 0x11000000;
int vd;
int space = 0x10D; // A32PRVDATA|D32 space
int data;
String driver = "vmehb";
Result result = new Result();
vd = vme.open(driver, space);
System.out.println("vd = "+vd);
data = 123456;
result = vme.writeInt(vd, vme_addr, data);
if(result.status() < 0)
System.out.println("writeInt:error occurred...");
result = vme.readInt(vd, vme_addr);
if(result.status() < 0)
System.out.println("readInt:error occurred...");
System.out.println("read data ="+result.data);
vme.close(vd);
return 0;
}
public static void main(String argv[]) {
HorbURL url = new HorbURL("-", "daemonServer");
Server_Proxy vme = new Server_Proxy(url);
int status = user(vme);
}
}
プログラムキットはhttp://www-online.kek.jp/~yasu/ATLAS/test-ROD/testROD-code.tar.gzからダウンロードできます。
struct vmedevice構造体はVMEアダプタ、AMコード、データアクセス幅、
などを指定する。この情報だけで/dev/vme...などのデバイスを特定できる。
struct vmedevice構造体はサーバ側とクライアント側が持つべき構造体である。
サーバにおいてはサービス可能なデバイスに対応するこの構造体を
予め定義しておく。クライアントにおいては呼び出すべきデバイスを
特定できるように予め決められたデバイス指定様式に基づきデバイスを
指定する。オープン時に呼び出され、クライアントVMEライブラリは
上記の情報に基づきmapping_device関数でデバイスを獲得し、
新たにvmedevicebox構造体を作成し、リンクリストに加える。
struct vmedevice {
char driver[12]; // ドライバ名
char device[16]; // デバイス名
int space; // AMコード及びデータアクセス幅
int vmeaddr; // マップすべきVMEアドレス
int size; // マップすべきアドレスサイズ(バイト単位)
};
例えば、次のようにする。
static struct vmedevice vmedevicelist[] = {
{"vmehb", "/dev/vme32d32", A32PRVDATA|D32, 0x11000000, 0x00100000},
{"vmehb", "/dev/vme24d16", A24PRVDATA|D16, 0x00000000, 0x00010000},
{"vmehb", "/dev/vme16d16", A16PRVDATA|D16, 0x00000000, 0x00010000},
{"ccihsc","/dev/vme24d32", A24NONPRVDATA|D32, 0x00800000, 0x00010000},
(int)NULL
};
これらのデータ構造体はVMEアクセスサーバ管理者が予め作成する。正しい設定を
行なわない限りサーバは立ち上がらない。
下記の構造体は内部で利用されるもので特にVMEアクセスサーバ管理者は扱うことはない。
struct vmedevicebox {
struct vmedevicebox *next;
struct vmedevice *vmedev;
int vd;
char *viraddr;
};
サーバ側のプログラムは サーバはあらかじめvmedevice構造体のリスト
からなるデータベースに従って初期化する。すべての初期化が成功しない限り、
立ち上がらない。VMEのopenに対しては要求された情報に対応する
プロトコル/AMコードなどを探し、見つかればOKの、見つからなければ、Fail の
ステータスを返す。また、VMEのread/writeに対しては要求された情報に対応する
プロトコル/AMコードを探し、それに対応した適切なアクセスを行う。
サーバにはクライアントの情報をデータベースとして管理しない。
従って、クライアントはいかなる時でも、こけてもかまわない。
サーバプログラムのアルゴリズムは次のようになる。
まずは初期化のアルゴリズム。
- vmedevice構造体のリストからvmedevicebox構造体のリストを作成する。
- そのリストに基づきデバイスをオープンする。
- オープンが成功したらろのリストをリンクリストに登録する。
- これをすべてのデバイスについて行ない、すべて成功したらクライアント
からの要求を待つ。
次にクライアントからのオープン要求に対するサーバの応答。
- クライアントからvmedevice構造体が送られて来る。
- その内容をチェックし、対応するデバイスがあること。VMEアドレスが
有効であること、VME空間が有効であること、アクセスする長さが有効であること
を確認し、OKをクライアントに返すと同時にVME IDを返す。
- もし、1つでも有効でなかったら、Failを返す。
クライアントからのread/write要求に対するサーバの応答。
- クライアントからVME IDとVMEアドレスと長さを受け取る。
- その値が有効であれば、対応する関数を呼ぶ。
- 有効でなければ、Failを返す。
- アクセスがreadであればデータを読み出す。writeであれば書き込む。
- 処理が終了したら、そのステータスをチェックする。
- OKならば、readであればデータとOKを、writeであれば、O<を返す。