390 likes | 400 Views
This training provides an introduction to vxWorks, highlighting the major differences between vxWorks and Unix/Linux. It covers programming techniques, calling C code from EPICS, soft device support, and more.
E N D
Running user C-Code on IOCs With special attention to vxWorks Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
Major differences between vxWorks and Unix/Linux • vxWorks has no programs but many threads (called "tasks"). • The whole IOC is one "program". • Parts of the IOC (modules, libraries, threads) are not independent. • If any part of the "program" crashes, the whole IOC does. • vxWorks has no users. • Everything runs as "root". (To be exact: in kernel space) • Everybody can do everything. • vxWorks is optimized for speed – not for safety. • You can overwrite memory, stack, interrupt tables, … • If you want something save you must make it save. Advanced EPICS Training, Dirk Zimoch 2009
Consequences of the "one program" concept • All functions exist in the same "space". • Name clashes may happen between different libraries • Use unique names (with prefix) for global functions! • Wrong: config, test, read_bi • Right: drvXyConfig, fooTest, devAbc_read_bi • Or make functions static. • vxWorks has no main function. • Any function (including the shell) can call any other function. • You don’t start programs from the shell, you call functions. • When a name clash happens, you might call the wrong function. Advanced EPICS Training, Dirk Zimoch 2009
Consequences of multi threading • Any problem in one thread affects the whole IOC. • System resources are global to the whole IOC. • Memory • File handles • Semaphores • Ending a thread does not clean up system resources. • The programmer (that's you!) must close files, free memory, etc. • Global data needs protection against concurrent access. • Global variables • VME access Advanced EPICS Training, Dirk Zimoch 2009
Boon and bane of unlimited memory access • Pro: Functions and threads can easily … • exchange large amounts of data by reference (pointers). • access any hardware register (e.g. VME bus). • Con: Functions and threads can easily … • overrun allocated memory or stack size (esp. with arrays) • overwrite system tables. (e.g. interrupt handler table at NULL) • overwrite program code. • modify global variables of other modules (e.g. drivers). Global variables are EVIL! Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
vxWorks help • Important for beginners:VxWorks Programmer's Guide, Chapter 2 • All about tasks, semaphores, watchdog timers, interrupts • Always helpful:vxWorks Reference Manual • All vxWorks system functions • Run-time help: Type help on the vxWorks shell. Advanced EPICS Training, Dirk Zimoch 2009
XTEST-VME-ID1 > help help Print this list ioHelp Print I/O utilities help info dbgHelp Print debugger help info nfsHelp Print nfs help info netHelp Print network help info spyHelp Print task histogrammer help info timexHelp Print execution timer help info h [n] Print (or set) shell history i [task] Summary of tasks' TCBs ti task Complete info on TCB for task sp adr,args... Spawn a task, pri=100, opt=0x19, stk=20000 taskSpawn name,pri,opt,stk,adr,args... Spawn a task td task Delete a task ts task Suspend a task tr task Resume a task d [adr[,nunits[,width]]] Display memory m adr[,width] Modify memory mRegs [reg[,task]] Modify a task's registers interactively pc [task] Return task's program counter Type <CR> to continue, Q<CR> to stop: Advanced EPICS Training, Dirk Zimoch 2009
Calling functions from the vxWorks shell • Never call your main function main! • Use a name you would give to a program on Linux. • The shell can pass up to 10 integer or string arguments. • float or double shell arguments don't work on PPC architectures. Don't use them. • No check is done by the shell. • Check all arguments for sanity (numeric ranges, NULL strings, …) in your function. • The shell can call functions in a separate task • sp function, arg1, … • repeatedly: repeat n, function, arg1, … • periodically: period seconds, function, arg1, … Advanced EPICS Training, Dirk Zimoch 2009
Examples • Setting or creating global variables drvXyDebug = 1 str = "This is a string" • Calling functions printf (“String: %s, number: %d\n”, str, drvXyDebug) • Note: Outermost parentheses are optional • Things that do not work • Non-functions C constructs: if, switch, for, while, … • Floating point: printf “%g\n”, 3.1415 • More than 10 parameters Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
Why global variables are evil (1) • Global variables with the same name in different modules are the same piece of memory. • Problem: Two modules may mutually overwrite their values. • Solution 1: Make variable local to source file with static. • Solution 2: Prefix global variable name with module name. Wrong Right /* internal variable */ int card_count; /* external variable */ int debug=0; /* internal variable */ static int card_count; /* external variable */ int drvXyDebug=0; Advanced EPICS Training, Dirk Zimoch 2009
Why global variables are evil (2) • All instances (of threads, drivers, SNL programs …) share the same global variable. • Problem: Two instances mutually overwrite their values. • Solution: Wrap variables in allocated struct per instance.Make liked list and store only hook in a static variable. Wrong Right /* values for one card */ static char* addr; static int ivec; /* linked list */ struct drvPriv { struct drvPriv *next; char* addr; int ivec; } drvPriv; static drvPriv *first=NULL; Advanced EPICS Training, Dirk Zimoch 2009
Debug and error messages are vital • Fail early and loud! • Be descriptive. • What happened where under which circumstances? • Bad: "error read" • Good: "drvXyReadInteger card 4 signal 2: read timeout after 5000 msec" • Write error and debug messages to stderr. • Make debug messages switchable (perhaps multiple levels). • global switch: int drvXyDebug=0; • message: if (drvXyDebug>=2) fprintf(stderr, …); Advanced EPICS Training, Dirk Zimoch 2009
Be paranoid! • Error checking is the key to a stable system. • Stability is limited by the weakest point! • Check arguments to API functions (esp. shell functions) • Never trust a user! Not even yourself. • Always check pointer arguments for validity. • Writing to NULL overwrites the interrupt handler table! • Check results of system functions (malloc, fopen, …) • System functions may fail and return NULL or ERROR. • Using these values unchecked may crash the system later. • Check for "cannot be" values (e.g. in case constructs) Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
Subroutine record sub • 12 input links INPA … INPL, 12 input fields A … L • Record copies from links to fields before calling user function. • Either use input link or write directly to input field. • All inputs and result are of type double. • User function can use A … L and writes result to VAL. • Field SNAM contains name of user function. • Field INAM contains name of optional init function. • Functions get pointer to record and have access to all fields. • Field names are lower case: a … l, val Advanced EPICS Training, Dirk Zimoch 2009
Subroutine record user function • Inputs are in fields a … l, output goes to val (all double) • Example: accumulate A*B to VAL #include <subRecord.h> int subAccu(struct subRecord* record) { record->val = record->val + record->a * record->b; return 0;} • Specify name of function in SNAM field of record. record (sub, "$(NAME)") { field (SNAM, "subAccu") field (INPA, "$(INPUT)") field (INPB, "$(SCALE)")} Advanced EPICS Training, Dirk Zimoch 2009
Subroutine record initialization • Optional init function int subAccuInit (subRecord* record) { record->val = 1.0; return 0;} • Specify init function name in INAM field. record (sub, "$(NAME)") { field (SNAM, "subAccu") field (INAM, "subAccuInit") ...} • Init function runs only once at boot time. Advanced EPICS Training, Dirk Zimoch 2009
Advanced: Asynchronous subroutine record • If function takes long time to complete... • Run calculation in separate work thread with low priority. • Setup thread in init function. • Store data for inter-thread communication in dpvt field. • Trigger work thread from record function. • Return 1 from record function to signal:“calculation not yet complete”. • Re-process record when calculation completes. • Use callbackRequestProcessCallback. • Field pact is 0 in first run and 1 in second run. • Return 0 from record function to signal:“calculation complete”. • Return other value (e.g. ERROR or errno) to signal failure. Advanced EPICS Training, Dirk Zimoch 2009
Asynchronous subroutine stub #include <subRecord.h>#include <callback.h>#include <taskLib.h>#include <semLib.h>#include <errno.h> /* private data for record (stored in dpvt field) */typedef struct { int status; /* error status */ double val; /* result */ SEM_ID trigger; /* trigger for work thread */ CALLBACK cb; /* callback for re-processing */} asyncSubPriv; void myAsyncSubThread(struct subRecord* record); int myAsyncSub(struct subRecord* record);int myAsyncSubInit(struct subRecord* record); Advanced EPICS Training, Dirk Zimoch 2009
Asynchronous subroutine work thread void myAsyncSubThread(struct subRecord* record) { asyncSubPriv* priv = record->dpvt; /* get private data */ while (1) { /* loop forever */semTake(priv->trigger, WAIT_FOREVER); /* wait */ /* do calculations */ /* leave result in priv->val */ /* leave error status in priv->status */ /* re-process record */ callbackRequestProcessCallback( &priv->cb, record->prio, record); }} Advanced EPICS Training, Dirk Zimoch 2009
Asynchronous subroutine user function int myAsyncSub(struct subRecord* record) { asyncSubPriv* priv = record->dpvt; /* get private data */ if (priv == NULL) { fprintf (stderr, "myAsynSub %s: record not initialized " "(INAM not set)\n", record->name); return ERROR; } if (record->pact == 0) { /* first run */ semGive(priv->trigger); /* trigger work thread */return 1; /* signal: not yet done */ } /*second run */ if (priv->status) { /* error in work thread */ fprintf (stderr, "myAsynSub %s: terrible failure: %s\n", record->name, myErrorString[priv->status]); return priv->status; } record->val = priv->val; /* update record */ return 0; /* signal: done */} Take care of useful error messages Advanced EPICS Training, Dirk Zimoch 2009
Asynchronous subroutine init function int myAsyncSubInit(struct subRecord* record) { int tid; SEM_ID trigger; asyncSubPriv* priv = malloc(sizeof(asyncSubPriv)); trigger = semBCreate(SEM_Q_FIFO, SEM_EMPTY); tid = taskSpawn("asyncSub", 200, VX_FP_TASK, 10000, (FUNCPTR) myAsyncSubThread, (int) record, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (!priv || !trigger || tid == ERROR ) { fprintf (stderr, "myAsyncSubInit %s: Out of memory\n", record->name); return errno; } priv->trigger = trigger; record->dpvt = priv; return 0;} Don't forget useful error message here Advanced EPICS Training, Dirk Zimoch 2009
General subroutine record genSub (compared to sub) • The genSub record is part of of SynApps, not EPICS base. • All inputs and outputs are arrays of user defined type. • Input links INPA … INPU and fields A … U • Output fields VALA … VALU and links OUTA … OUTU • Input/output data types FTA … FTU, FTVA … FTVU • One of CHAR, SHORT, LONG, ULONG, FLOAT, DOUBLE, … • Input/output element count NOA … NOU, NOVA … NOVU • Always set FT* and NO* fields of all used fields! • Fields SNAM and INAM similar to sub record. • Asynchronous user function is not supported. Advanced EPICS Training, Dirk Zimoch 2009
General subroutine record user function • Input and output fields a … u, vala … valu are void*. • Cast void* to correct pointer type. • This easily crashes the IOC if ft* and no* fields are wrong! • Always check field type and size! • Do not process if type or size is wrong. Exit with error message. • Fields are pointers to arrays, even if element count is 1. • Checking every time the record processes is expensive. • Check only once in init function (when IOC boots). • Do not process record if check failed. • Danger of crashing IOC is much higher than with sub record! Advanced EPICS Training, Dirk Zimoch 2009
General subroutine record init function • Check all data types and element counts. • Field types are menuFtypeSHORT, menuFtypeDOUBLE, … • Print descriptive error message if check fails! • Initialize any other private data (buffers, etc…) • Assign structure to dpvt field only if all checks succeed. • Even if no private data is needed, set dpvt to a dummy value if check succeeds. • Check dpvt field at start of user function. • Never process the record if dpvt is not set! Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
Soft device support • Available for "standard" I/O records • ai, bi, mbbi, waveform, … • Makes a new DTYP choice available • Just like "Soft Channel" and "Raw Soft Channel" • Only one input (INP) and one output (VAL) • Examples: • Timestamp for stringin (INP contains format string) • File read for waveform (INP contains file name) • FFT for waveform (INP points to other waveform) • Integration for waveform (INP points to other waveform) Advanced EPICS Training, Dirk Zimoch 2009
Writing soft device support • Write two functions: init_record and read • Very similar to sub record • Define a global device support function tablestruct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read;} devIntegrateWaveform = … • Write dbd file to make function table known device(waveform, CONSTANT, devIntegrateWaveform, "integrate") device support table name record type Link type, CONSTANT means: "constant or link to record" DTYP string Advanced EPICS Training, Dirk Zimoch 2009
Example soft device support: Integrate waveform #include <recGbl.h>#include <devSup.h>#include <alarm.h>#include <dbAccess.h>#include <waveformRecord.h> long devIntegrateWaveformInit(waveformRecord *record){ switch (record->inp.type) { case (PV_LINK): case (DB_LINK): case (CA_LINK): break; default: recGblRecordError(S_db_badField, record, "devIntegrateWaveform (init_record) Illegal INP field"); return S_db_badField; } return 0;} Advanced EPICS Training, Dirk Zimoch 2009
Example soft device support: Integrate waveform long devIntegrateWaveformRead(waveformRecord *record){ long status, n, i; n = record->nelm; status = dbGetLink(&record->inp, record->ftvl, record->bptr, 0, &n); if (status) { recGblSetSevr(record, READ_ALARM, INVALID_ALARM); return status; } record->nord = n; switch (record->ftvl) { case DBF_DOUBLE: { double sum = 0.0; for (i=0; i<n; i++) { sum += ((double*)(record->bptr))[i]; ((double*)(record->bptr))[i] = sum; } break; } /* case ... (other DBF types) */ } return 0;} Advanced EPICS Training, Dirk Zimoch 2009
Example soft device support: Integrate waveform #include <epicsExport.h> struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read;} devIntegrateWaveform = { 5, NULL, NULL,devIntegrateWaveformInit, NULL,devIntegrateWaveformRead}; epicsExportAddress (dtyp, devIntegrateWaveform); Put in the two functions you wrote Advanced EPICS Training, Dirk Zimoch 2009
Contents • vxWorks intro • Major differences between vxWorks and Unix/Linux • Using the vxWorks shell • Programming techniques • Calling C code from EPICS • Subroutine records sub and genSub • Soft device support • State notation language Advanced EPICS Training, Dirk Zimoch 2009
State Notation Language • State machine implementation for EPICS • Do something when event happens • "Events" are CA monitors (record changes) or timeout • "Do something" can be any C-code • C-like syntax • Understands many C functions and statements • Escapes to "real" C-code for special occasions • Easy to use CA interface • pvPut, pvGet, monitor • Any number of input and output records Advanced EPICS Training, Dirk Zimoch 2009
program coolingswitch int cooling;assign cooling to "{DEV}:COOLING"; double temp;assign temp to "{DEV}:TEMP";monitor temp; ss coolingswitch { state cold { when (temp>25.3) { cooling = 1; pvPut(cooling); } state hot } state hot { when (temp<22.0) { cooling = 0; pvPut(cooling); } state cold }} Simple SNL Example start state "cold" [temp>25.3°] / cooling on [temp<22°] / cooling off state "hot" Advanced EPICS Training, Dirk Zimoch 2009
Escape single line with %%... especially #include Escape block with %{...}% Avoid accessing "global" SNL variables from within escaped C code. Implementation depends on "+r" flag program calculator %%#include <math.h> %{ void myCalc( double i1, double i2, double* o1, double* o2) { *o1 = sin(i1 + i2); *o2 = cos(i1 – i2); }}% Including C-code into SNL Advanced EPICS Training, Dirk Zimoch 2009
double in1;double in2;double out1;double out2; assign in1 to "{DEV}:INPUT1";assign in2 to "{DEV}:INPUT2";assign out1 to "{DEV}:OUTPUT1";assign out2 to "{DEV}:OUTPUT2" monitor in1;monitor in2; evflag newInput;sync in1 to newInput;sync in2 to newInput; ss calculator { state idle { when (efTestAndClear(newInput)) {myCalc(in1, in2, &out1, &out2); pvPut(out1); pvPut(out2); } state idle }} "Abusing" SNL for calculations start idle [newInput] / myCalc() Advanced EPICS Training, Dirk Zimoch 2009