EPICS record/device/driver Support ,

Download Report

Transcript EPICS record/device/driver Support ,

EPICS
record/device/driver
Support
[email protected],
many slides copied from
[email protected]
Interfacing Hardware
General Idea
Software
EPICS
IOC Core: Db, CA, …
Record Support
“Driver”
Hardware
Device Support
Driver Support
Hardware
Where to extend…

Common Case: New Hardware (I/O Board,..)



Sometimes: Specialized Record


Driver: Any low-level code to talk to the hardware.
Might not have any knowledge of EPICS
Device: EPICS-specific glue code between driver
and (subset of) records
Copy of existing record w/ slight change
Seldom: New Record Type


Can task be handled by combination of existing
records, maybe w/ help of SNL?
But sometime it is fun to introduce a new
record(NY).
Driver/Device/Record Support


Read the “IOC Application Developer's Guide” before
even thinking about doing this!
Common Idea:





Describe the new driver/device/record via DBD (Database
Description File, ASCII)
Implement the functionality, providing a function
table(DRVET,DSET or RSET) specific to driver/device/record
support (init(), report(), do_something(), …)
Link/load the binaries which export a function table
During startup, iocCore will parse the DBD, locate the
function table and invoke the appropriate functions
Well-defined interfaces for adding new support,
minimal recompilation.
The .dbd file entry

.dbd file includes

Record type definition
recordtype(xxx)
{include "dbCommon.dbd"
field(VAL,DBF_DOUBLE)
{
prompt("Current EGU Value")
asl(ASL0)
pp(TRUE)
}
…
}
name of RSET for the record xxx have to be xxxRSET

Device support link to a record type
device(recType,addrType,dset,"name")
Driver declaration
driver(drvet)

The .dbd file entry

The IOC discovers what device supports are present from
entries in the .dbd file
device(recType,addrType,dset,"name")

addrType is one of
AB_IO
CAMAC_IO
RF_IO

dset is the ‘C’ symbol name for the Device Support Entry
Table (DSET)



BITBUS_IO
BBGPIB_IO
GPIB_IO INST_IO
VME_IO VXI_IO
By convention the dset name indicates the record type and hardware
interface
This is how record support and the database code call device support
routines
For example
device(ai,INST_IO,devAiSymb,"vxWorks variable")
device(bo,VME_IO,devBoXy240,"Xycom XY240")
Record/Device/Driver
Tek4320 HPXXX
AI
AO
LI
devAITe …
k4320
devAOTe …
k4302
…
…
Driv GP-IB
er
PS
contr.
…
AdVME Td4V
1521
…
…
…
…
…
…
…
…
ARCne VME
t
Driver Support

Drivers can be complicated:


Bus-level access, critical timing, interrupts,
semaphores, threads, deadlocks, fail-safe,
OS-specific (vxWorks, Linux, Win32), …
Typical collection of routines:
Check, report, init, read, write,
setup_trigger(callback),…

“EPICS part”: Optional & Trivial!
Driver Support Entry Table
/* 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 */
};

Any routine pointer can be NULL
DRVET Example
/* 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
};
Registering Driver w/ EPICS

EPICS DBD File Entry


driver(drvXy)
Results:


EPICS iocInit will locate “drvXy” in
symbol table, call the registered “init”
routine (unless NULL)
EPICS dbior will invoke “report” routine
(unless NULL)
Good Practice


Wrong: Assume e.g. two XY board, one at
base address 0x1234 and one at 0x4567, all
hard-coded in driver.
Best: Provide “configure” routine, callable
from e.g. vxWorks startup file before invoking
iocInit:
# EPICS records that refer to XY #0 will
# access board at base addr. 0xfe12
# in mode 15
xy_config(0, 0xfe12, 15)
Device Support

Glue between record and driver is highly
specific to the resp. record:


AI record, DTYP=“XY”, INP=“#C0 S5”:
Device support has to call driver for “XY” card #0
and get signal #5 into the record’s RVAL field
Dev.sup routines common to all records:
Report: Show info
 Init: Called once
 Init_Record: Called for each record
 Get I/O Interrupt Info: Used w/ SCAN=“I/O Intr”
Most are optional, but check specific record type.

Device Support Entry Table
/* 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
*/
}
Registering Device Support

EPICS DBD File:


device(ai,INST_IO,devAiXX,“My XX")
Result: iocCore …


now allows DTYP=“My XX” for ai records
will locate DSET “devAiXX” in symbol table,
call init(), then init_record() for each
record, record will call read() whenever
record gets processed, …
Before Implementing Dev.Sup


Understand how to use the driver
Read
“Application Developer Guide”
 Source for specific record type XX,
understand how record calls its
device support
 Examples in EPICS base sources:
base/src/dev/softDev/devXXSoft.c

Device Support for AI Record

Common




report
initialization
initialize instance
attach to device
interrupt

AI-Specific


read ai device value
linear conversion
(RVAL->VAL)
AI Device Support Type
Initialization
long aiDevInit (unsigned pass)
 common to all record types
 device specific initialization
 pass = 0, prior to initializing each record
during "iocInit()“


Check hardware, …
but note that records are not ready to handle data
pass = 1, after initializing each record during
"iocInit()“

Activate triggers, …
AI Device Report
long aiDevReport (struct aiRecord *
pai, int level);




common to all records, but gets passed
pointer to specific record type
called once for every record instance when
the user types "dbior <level>"
device status to "stdout" from this routine
Idea: detail increases with increasing "level"
AI Device Initialization for Record
long aiDevInitInstance(struct aiRecord *pai)


Called from within “iocInit()” once for each
record attached to device
Typical


Parse & check device address (pai->inp)
Store device-data, e.g. the parsed signal #, driver
handles, … in 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;
Read Signal Value
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);
}
AI Linear Conversion
long aiDevLinearConv (
struct aiRecord *pai, int after);

Setup the slope and offset for the conversion
to engineering units
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 */
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;
Advanced: Interrupts

Device supports interrupts, want to use SCAN=“I/O
Intr”



Difficulty



higher scan rate
scan synchronized with device
Interrupts: interrupt level, most OS routines prohibited
Record processing: task level
IOSCANPVT

EPICS Core Helper for processing records in response to
interrupt
IOSCANPVT

Initialize & keep one IOSCANPVT per distinct
interrupt that the hardware can generate
/* Record’s DPVT points to struct X
* which contains IOCSCANPVT ioscanpvt */
X = (X *) rec->dpvt;
scanIoInit(&X->ioscanpvt);

Each Interrupt Occurrence (ISR):
scanIoRequest(X->ioscanpvt);

safe to call from ISR, but don’t call
scanIoRequest() until after database init
(“iocInit()”) completes
(extern volatile int interruptAccept;)
Provide IO Interrupt Info
long aiDevGetIoIntInfo (
int cmd,
struct aiRecord *pai,
IOSCANPVT *ppvt);
associates interrupt source with record
*ppvt = X->ioscanpvt;
 cmd==0 - insert into IO interrupt scan
 cmd==1 - remove from IO Interrupt
scan

Asynchronous Devices



read/write routine sets “PACT” true and
returns zero for success.
asynchronous IO completion callback
completes record processing
don’t process a record from within an
ISR
Example Asynchronous Read
long devXxxRead (struct aiRecord *pai) {
if (pai->pact)
return S_devXxx_OK;
/* zero */
pai->pact = TRUE
devXxxBeginAsyncIO(pai->dpvt);
return S_devXxx_OK;
}
Example Asynchronous Read
Completion
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);
}
Record Support

Similar to device & driver support:



Certain routines need to be implemented
Binary exports a “Record support entry table”
which contains those routines
DBD File to describe record and each field
(name, data type,
maybe menu of enumerated values, …)
Record DBD File


Need to read “IOC Application Developer's Guide” to
understand full DBD syntax!
Use xxxRecord created by makeBaseApp as example
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)
}
…
}
Record support entry table


Initialization:
General and per-record instance
Process routine:
Implements functionality of record. Often





calls device support specific to this record type
checks for alarms
posts monitors
processes forward links
Utility routines that allow iocCore to properly
read, write & display fields of this record
Record support entry table…

Record Implementation must export
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;
};
Initialization

init()


Called once during IOC startup
init_record(void *precord, int pass)

Called twice per record instance. Second
pass can affect other records.
Processing

Usually follows this 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);
}
Utility Routines

Allow to define


how the record responds when fields are
read or written
the units, precision, limits;
not only of the VAL field!
Utility Routines…

special(DBADDR *addr, int after)


Allows us to react before/after somebody else
accesses field ref’ed by addr.
get_units(DBADDR *addr, char *units),
get_precision(DBADDR *addr, long *prec),
get_array_info(…)

Can simply copy contents of EGU or PREC fields,
can also provide information specific to field ref’ed
by addr