Transcript
Page 1: EPICS レコード/デバイス / ドライバ サポート

EPICSレコード/デバイス /ドライ

バサポート

[email protected],many slides copied from

[email protected]

Page 2: EPICS レコード/デバイス / ドライバ サポート

ハードウェアとの接続

Hardware

“Driver”

Software

General Idea

Hardware

Device Support

IOC Core: Db, CA, …

EPICS

Driver Support

Record Support

Page 3: EPICS レコード/デバイス / ドライバ サポート

どこを拡張するのか… よくある場合 : 新しいハードウェア (I/O

Board,..) ドライバ : ハードウェアに直接アクセスする低レベルの

ソフトウェア。 EPICS について何も関知しない。 デバイス :EPICS 特有のドライバレコード(のサブセッ

ト)をつなぐ糊ソフトウェア 時々 : 特定目的レコード

既存のレコードサポートを少し変えてつくる。 まれに : 新しいレコードタイプ

SNL や既存のレコードを組み合わせてできないだろうか?

Page 4: EPICS レコード/デバイス / ドライバ サポート

ドライバ/デバイス/レコード これらについて何かしようと考える前に、“ IOC

Application Developer’s Guide” を読むこと ! 共通の考え方 :

新しいドライバ /デバイス /レコードを DBD ファイル(Database Description File, ASCII) に記述する

機能を実装し、ドライバ /デバイス /レコードサポートに特有な関数テーブルを用意する。

関数テーブルを外部参照とするコンパイル済みバイナリをLink/load する。(関数自体は static な関数となる)

初期化時に iocCore/iocsh は DBD ファイルを解釈し、関数テーブルを設置し、適切な関数を実行する。

新しいサポートを追加するためのインタフェースはよく定義されており、再コンパイルは最小限でよい。

Page 5: EPICS レコード/デバイス / ドライバ サポート

ドライバサポート ドライバは一般に複雑になる :

Bus-level access, critical timing, interrupts, semaphores, threads, deadlocks, fail-safe, OS-specific (vxWorks, Linux, Win32), …

典型的な関数の集合体 :Check, report, init, read, write, setup_trigger(callback),…

“EPICS part”: オプショナル & 自明 !

Page 6: EPICS レコード/デバイス / ドライバ サポート

ドライバ・サポート・エントリ・テーブル (DRVET)

/* EPICS Base include file <drvSup.h> */

typedef long (*DRVSUPFUN) ();

struct drvet

{

long number; /*number of support routines*/DRVSUPFUN report; /*print report*/DRVSUPFUN init; /*init the driver */

};

どの関数ポインタも NULL とすることができる。

Page 7: EPICS レコード/デバイス / ドライバ サポート

DRVET の例/* xy.c */#include<drvSup.h>

static long xy_report(){

printf(“XY Driver Info:\n); …} static long xy_init(){

if (xy_check()) { …} struct drvet drvXy ={

2, xy_report,

xy_init};

Page 8: EPICS レコード/デバイス / ドライバ サポート

EPICS へのドライバの登録 EPICS DBD File への登録

driver(drvXy) 結果 :

EPICS iocInit は” drvXy” をシンボルテーブルから探し出し、その中の” init” ルーチンを呼び出す。

VxShell/iocsh で dbior を使うと “ report” ルーチンを実行する。

Page 9: EPICS レコード/デバイス / ドライバ サポート

よい習慣 Wrong:2 枚の XY board があり一枚の base

address は 0x1234 でもう一枚は 0x4567二あると仮定し、ドライバプログラムに書き込んだ。

Best: “configure” ルーチンを用意し、 vxWorks/iocsh のスタートアップファイルでiocInit が実行される前にこの” conifugre” 関数を実行する。

# EPICS records that refer to XY #0 will

# access board at base addr. 0xfe12# in mode 15xy_config(0, 0xfe12, 15)

Page 10: EPICS レコード/デバイス / ドライバ サポート

デバイス サポート レコードとドライバをつなぐ糊ソフトウェアでレコード型それぞれに特

有のものとなる。(デバイス型xレコード型) : AI record, DTYP=“XY”, INP=“#C0 S5”:

デバイスサポートは” XY” のドライバをカード #0 にたいして呼び出し、信号 #5 を読み出してレコードの RVAL に収める。

デバイス・サポートの名前は、 debAiSoft.c などのようにdev<RecType><Device>.c とするのが慣習。

すべてのレコードに共通な Dev.Sup. 関数 : Report: 情報を表示 Init: 初期化、一度だけ呼ばれる。 Init_Record: レコード毎に呼ばれる。 Get I/O Interrupt Info: SCAN=“I/O Intr” で使われる。これらは省略可能であるが、それぞれのレコード型についてチェックが

必要。

Page 11: EPICS レコード/デバイス / ドライバ サポート

デバイス・サポート・エントリ・テーブル (DSET)

/* Defined in devSup.h */struct dset{

long number; /* number of support routines */DEVSUPFUN report; /* print report*/DEVSUPFUN init; /* init support*/DEVSUPFUN init_record; /* init particular record */DEVSUPFUN get_ioint_info; /* get I/O Intr. Info *//* Rest specific to record, e.g. BI: */ DEVSUPFUN read_bi;/* Result: (0,2,error) 0 -> raw value stored in RVAL, convert to VAL 2 -> value already stored in VAL, don’t convert

*/}

Page 12: EPICS レコード/デバイス / ドライバ サポート

デバイス・サポートの登録 EPICS DBD ファイル :

device(ai,INST_IO,devAiXX,“My XX") 結果 : iocCore …

DTYP=“My XX” を ai records で指定できる。

DSET “devAiXX” をシンボル・テーブルから見つけ出し、 init() 関数を実行し、それぞれのレコードについて init_record() を実行する。 レコードが「処理」される毎にread() 関数が実行される。

Page 13: EPICS レコード/デバイス / ドライバ サポート

Dev.Sup を実装する前に ドライバの使い方を理解しておくこと。 以下の文献を読むこと:

“Application Developer Guide” 対象となるレコード XX のソースコードを

よみ、どのようにレコードサポートルーチンがデバイスサポートを呼び出すかを理解すること。

EPICS base のサンプルを読む : base/src/dev/softDev/devXXSoft.c

Page 14: EPICS レコード/デバイス / ドライバ サポート

AI レコードのデバイスサポート 共通

report initialization initialize instance 機器からの割り込み

AI 特有 Read : ai レコー

ドにデバイスの値を読み込む

線形変換(RVAL->VAL)

Page 15: EPICS レコード/デバイス / ドライバ サポート

AI Dev. Sup. の初期化

long aiDevInit (unsigned pass) すべてのレコード型に共通 デバイス特有の初期化 pass = 0, “iocInit()“ ですべてのレコードの

初期化前に呼ばれる。 HW のチェックなど、ただし、レコードはまだ

データを取り扱える状態ではない。 pass = 1, “iocInit()“ 中で個々のレコードの

初期化が終わった後にもう一度呼び出される。 トリガを有効にするなど

Page 16: EPICS レコード/デバイス / ドライバ サポート

AI デバイス レポート

long aiDevReport (struct aiRecord * pai, int level);

すべてのレコードに共通。ただし特有のレコード型へのポインタを引数としてとる。

ユーザが” dbior <level>” を実行した際に、それぞれのレコードの実体にたいして一度ずつ呼び出される。

デバイス状態を stdout に出力する。 Idea: “level” をあげることでより詳細な情

報を得る。

Page 17: EPICS レコード/デバイス / ドライバ サポート

レコード毎の AI デバイスの初期化long aiDevInitInstance(struct aiRecord *pai)

“iocInit()” 中でデイスに接続されているレコードのそれぞれについて 一度ずつ呼び出される。

典型的な役割 デバイスアドレス (pai->inp) の解釈とチェック デバイス固有データ(シグナル#、ドライバハン

ドラなど)の DPVT フィールドへの保存、:pvt = (X *) calloc(1, sizeof(X));pvt->signal = signal_this_record_wants;pvt->drv = magic_handle_we_got_from_driver;pai->dpvt = (void *) pvt;

Page 18: EPICS レコード/デバイス / ドライバ サポート

信号の値を読むlong aiDevRead_(struct aiRecord * pai){

long rval;if (device OK){

rval=pDevMemoryMap->aiRegister[pai->dpvt->signal];

pai->rval = rval;}else

recGblSetSevr(pai, READ_ALARM, INVALID_ALARM);

}

Page 19: EPICS レコード/デバイス / ドライバ サポート

AI 線形変換long aiDevLinearConv (

struct aiRecord *pai, int after); 制御値への変換のスロープとオフセットの設

定if (!after) return S_XXXX_OK;

/* A 12 bit DAC is assumed here */pai->eslo = (pai->eguf - pai->egul)/0x0FFF;pai->roff = 0;

/* roff could be different for device w/ e.g. +-5V, half scale = 0V */

Page 20: EPICS レコード/デバイス / ドライバ サポート

From convert() in aiRecord.c

double val;val = pai->rval + pai->roff;/* * adjust with slope/offset * if linear convert is used */if ( pai->aslo != 0.0 ) val *= pai->aslo;if( pai->aoff != 0.0 ) val+= pai->aoff;

if(pai->linr == menuConvertLINEAR) val = (val * pai->eslo) + pai-

>eoff;

Page 21: EPICS レコード/デバイス / ドライバ サポート

Advanced: 割り込み

Device supports interrupts, want to use SCAN=“I/O Intr”

高速なレコードのスキャン デバイスと同期したスキャン

困難な点: 割り込み : interrupt level で動作 , ほとんどの OS 機能は

使えない。 Record 処理 : 通常タスクレベルで動作

IOSCANPVT 割り込みに応答してレコードの処理を行うために EPICS

Core  に用意された仕組み

Page 22: EPICS レコード/デバイス / ドライバ サポート

IOSCANPVT

独立した割り込み要因毎に IOSCANPVT構造体を用意し、初期化しておく。/* Record’s DPVT points to struct X* which contains IOCSCANPVT ioscanpvt */X = (X *) rec->dpvt;scanIoInit(&X->ioscanpvt);

割り込み処理ルーチン (ISR) の中では :

scanIoRequest(X->ioscanpvt); ISR の中から呼んでも安全である。 ただし、 iocInit が終了する前(データベースの初期化完了

前)に scanIoRequest() が呼ばれてはならない。 (extern volatile int interruptAccept;)

Page 23: EPICS レコード/デバイス / ドライバ サポート

IO 割り込みの情報を提供long aiDevGetIoIntInfo (

int cmd, struct aiRecord *pai,IOSCANPVT *ppvt);

割り込みソースとレコード関連づける*ppvt = X->ioscanpvt; cmd==0 - IO 割り込みによるスキャ

ンに登録 cmd==1 - 登録から外す

Page 24: EPICS レコード/デバイス / ドライバ サポート

非同期 デバイス

read/write 関数は “ PACT” を true に設定し、0を正常終了として返す。

非同期 IO 終了コールバックがレコードの「処理」を終了させる。(PACT=False)

ISR(Interupt Service Routine) 中ではレコード処理関数を呼び出してはいけない。

Page 25: EPICS レコード/デバイス / ドライバ サポート

非同期読み込みの例long devXxxRead (struct aiRecord *pai) {

if (pai->pact)

return S_devXxx_OK; /* zero */

pai->pact = TRUE

devXxxBeginAsyncIO(pai->dpvt);

return S_devXxx_OK;

}

Page 26: EPICS レコード/デバイス / ドライバ サポート

非同期読み込み完了の例

void devXxxAsyncIOCompletion(struct aiRecord *pai, long ioStatus)

{

struct rset *prset = (struct rset *) pai->rset;

dbScanLock(pai);

if (ioStatus != S_devXxx_OK) {

recGblSetSevr(pai, READ_ALARM, INVALID_ALARM);

}

(*prset->process)(pai);

dbScanUnlock(pai);

}

Page 27: EPICS レコード/デバイス / ドライバ サポート

レコードサポート

デバイス /ドライバサポートと類似している: いくつかの関数は実装されなければならない。 コンパイルされたバイナリファイルは“ Record

support entry table” を外部参照としてもつ。 レコードとそれぞれのフィールド (name, data

type,maybe menu of enumerated values, …)の情報は .DBD ファイルに定義される。

Page 28: EPICS レコード/デバイス / ドライバ サポート

レコード DBD ファイル 完全な DBD の文法については、 “ IOC Application

Developer’s Guide” を読む必要がある ! makeBaseApp でつくられる xxxRecord を例にとる

recordtype(xxx){

# Each record needs to start w/ the common fields!include "dbCommon.dbd" field(VAL,DBF_DOUBLE)

{prompt("Current EGU Value")asl(ASL0)pp(TRUE)

}…

}

Page 29: EPICS レコード/デバイス / ドライバ サポート

レコード・サポート・エントリ・テーブル: RSET 初期化 :

レコードタイプ毎の初期化と、レコード毎の初期化 プロセス関数 :

レコードの機能を実装する。 . Often このレコード特有のデバイスサポートを呼び出す アラームのチェック モニタの掲示 フォワードリンクの処理

ユーティリティ関数を用意することで、 iocCore が正しく読み書きされようにする。

Page 30: EPICS レコード/デバイス / ドライバ サポート

レコード・サポート・エントリ・テーブル (RSET) レコードの実装は

struct rset <xxxRecord>RSET;を外部参照におく。struct rset /* record support entry table */{

long number; /* number of support routine */RECSUPFUN report; /* print report */RECSUPFUN init; /* init support */RECSUPFUN init_record; /* init record */RECSUPFUN process; /* process record */RECSUPFUN special; /* special processing */RECSUPFUN get_value; /* OBSOLETE: Just leave NULL */RECSUPFUN cvt_dbaddr; /* cvt dbAddr */RECSUPFUN get_array_info;RECSUPFUN put_array_info;RECSUPFUN get_units;RECSUPFUN get_precision;RECSUPFUN get_enum_str; /* get string from enum */RECSUPFUN get_enum_strs; /* get all enum strings */RECSUPFUN put_enum_str; /* put enum from string */RECSUPFUN get_graphic_double;RECSUPFUN get_control_double;RECSUPFUN get_alarm_double;

};

Page 31: EPICS レコード/デバイス / ドライバ サポート

初期化 init()

IOC 起動時に一回呼ばれる。 init_record(void *precord, int pass)

一つのレコードにつき一回ずつ呼ばれる。 . Second pass can affect other records.

Page 32: EPICS レコード/デバイス / ドライバ サポート

処理 通常はこの Example に従えばよい。static long process(void *precord){

xxxRecord*pxxx = (xxxRecord *)precord;xxxdset *pdset = (xxxdset *)pxxx->dset;long status;unsigned char pact=pxxx->pact;if( (pdset==NULL) || (pdset->read_xxx==NULL) ){

/* leave pact true so that dbProcess doesnt call again*/pxxx->pact=TRUE;recGblRecordError(S_dev_missingSup, pxxx, ”read_xxx”);return (S_dev_missingSup);

}/* pact must not be set true until read_xxx completes*/status=(*pdset->read_xxx)(pxxx); /* read the new value *//* return if beginning of asynch processing*/if(!pact && pxxx->pact) return(0);pxxx->pact = TRUE;recGblGetTimeStamp(pxxx);/* check for alarms */alarm(pxxx);/* check event list */monitor(pxxx);/* process the forward scan link record */recGblFwdLink(pxxx);pxxx->pact=FALSE;return(status);

}

Page 33: EPICS レコード/デバイス / ドライバ サポート

ユーティティ関数 次の動作を実装できる。

フィードが読み書きされたときレコードはどう反応すればよいか

ユニット、精度、リミット; VAL フィールドだけではない。

Page 34: EPICS レコード/デバイス / ドライバ サポート

ユーティリティ関数… special(DBADDR *addr, int after)

Addr で指定されたフィールドに誰かがアクセスする前後にとるアクションを記述できる。

get_units(DBADDR *addr, char *units),get_precision(DBADDR *addr, long *prec),get_array_info(…) EGU あるいは PREC フィールドの値を返すだけで

ある。また addr で指定されるフィールドに特有の情報を提供する。


Recommended