アトラス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

C Calling Sequence

C++ Calling Sequence

Java Calling Sequence

ユーザサンプルプログラム

C

これは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++

これは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;

}

C++/CORBA

これは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

これは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 and struct vmedevicebox

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コードを探し、それに対応した適切なアクセスを行う。 サーバにはクライアントの情報をデータベースとして管理しない。 従って、クライアントはいかなる時でも、こけてもかまわない。

サーバプログラム

サーバプログラムのアルゴリズムは次のようになる。 まずは初期化のアルゴリズム。 次にクライアントからのオープン要求に対するサーバの応答。 クライアントからのread/write要求に対するサーバの応答。