1 / 58

Writing C code for IOCs

Writing C code for IOCs. 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 Compiling C code for IOCs

rufus
Télécharger la présentation

Writing C code for IOCs

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Writing C code for IOCs Advanced EPICS Training, Dirk Zimoch 2009

  2. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  3. 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

  4. Consequences of the "one program" concept • All functions exist in the same "space". • Name clashes may appear between different modules (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. • Every function (including the shell) can call any other function. • You don’t start programs from the shell, you call functions. • When name clash happens, you might call the wrong function. Advanced EPICS Training, Dirk Zimoch 2009

  5. 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

  6. 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

  7. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  8. Accessing the vxWorks shell at SLS • Type rmc iocname, e.g. rmc MTEST-VME-T1. • The SLS specific command rmc stands for "remote minicom". • It does ssh to a central server. • It starts minicom on the server. • The server is connected to the DEBUG port of the IOC. • You must be on the same network as the IOC. • You may need your AFS password or the slsop password. • If the IOC is connected to local Linux PC, use minicom. • Serial line settings: 9600 baud, 8N1, no hardware handshake • Windows: hyperterm.exe (buggy) or Terminal.exe Advanced EPICS Training, Dirk Zimoch 2009

  9. vxWorks help • Online help: http://vxworks.web.psi.ch • 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. • Separate talk on vxWorks debugging? Advanced EPICS Training, Dirk Zimoch 2009

  10. 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

  11. Calling functions from the vxWorks shell • Never call your main function main! • Use a specific name, the name you would give 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. • No check is done by the shell. • Check all arguments for sanity (numeric ranges, NULL strings, …). • 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

  12. 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 • C constructs (non-functions) like: if, switch, for, while, … • Floating point: printf “%g\n”, 3.1415 • More than 10 parameters Advanced EPICS Training, Dirk Zimoch 2009

  13. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  14. Why global variables are evil (1) • Global variables with the same name in different modules are the same piece of memory. • Problem: Two different modules may mutually overwrite their values. • Solution 1: Make variable local to one 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

  15. 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 a struct, allocate one struct per instance. 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

  16. Debug and error messages are vital • Fail early and loud! • Make messages 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

  17. 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 much later. • Check for "impossible" values (e.g. in case constructs) Advanced EPICS Training, Dirk Zimoch 2009

  18. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  19. 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. • Input fields are all of type double. • User function can use A … L and writes result to VAL. • SNAM field contains name of user function. • INAM field 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

  20. 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

  21. 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

  22. 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. • pact field 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

  23. 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

  24. 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

  25. Asynchronous subroutine user function int myAsyncSub(struct subRecord* record) { asyncSubPriv* priv = record->dpvt; /* get private data */ if (priv == NULL) { return ERROR; } /* INAM missing */ 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 */ return priv->status; } record->val = priv->val; /* update record */ return 0; /* signal: done */} add error messages here Advanced EPICS Training, Dirk Zimoch 2009

  26. Asynchronous subroutine init function int myAsyncSubInit(struct subRecord* record) { int tid; SEM_ID trigger; asyncSubPriv* priv = malloc(sizeof(asyncSubPriv)); if (priv == NULL) { return errno; } priv->trigger = semBCreate(SEM_Q_FIFO, SEM_EMPTY); if (priv->trigger == NULL) { return errno; } tid = taskSpawn("asyncSub", 200, VX_FP_TASK, 10000, (FUNCPTR) myAsyncSubThread, (int) record, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (tid == ERROR) { return errno; } record->dpvt = priv; return 0;} add error messages here Advanced EPICS Training, Dirk Zimoch 2009

  27. General subroutine record genSub (compared to sub) • 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 inputs and outputs! • SNAM and INAM fields similar to sub record. • Asynchronous user function is not supported. • The genSub record must be loaded: require "SynApps" Advanced EPICS Training, Dirk Zimoch 2009

  28. General subroutine record user function • Input and output fields a … u, vala … valu are void*. • Fields are pointers to arrays, even if element count is 1. • 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. • Danger of crashing IOC is much higher than with sub record! • Checking every time the record processes is expensive. • Check only once in init function (when IOC boots)! • Do not process record after check failed! Advanced EPICS Training, Dirk Zimoch 2009

  29. General subroutine record init function • Check all data types and element counts. • Field types are one of 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. • If no private data is needed, set dpvt to a dummy value. • Check dpvt field at start of user function. • Do not process if dpvt is not set. Advanced EPICS Training, Dirk Zimoch 2009

  30. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  31. 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

  32. Writing device support • Write record init and read function • Define a global device support function table struct { 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") the two essential functions device support table name Link type, CONSTANT means: "constant or link to record" record type DTYP string Advanced EPICS Training, Dirk Zimoch 2009

  33. 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

  34. 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 ... */ } return 0;} Advanced EPICS Training, Dirk Zimoch 2009

  35. Example soft device support: Integrate waveform struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read;} devIntegrateWaveform = { 5, NULL, NULL,devIntegrateWaveformInit, NULL,devIntegrateWaveformRead}; Advanced EPICS Training, Dirk Zimoch 2009

  36. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  37. 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

  38. 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 }} Using SNL start state "cold" [temp>25.3°] / cooling on [temp<22°] / cooling off state "hot" Advanced EPICS Training, Dirk Zimoch 2009

  39. 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

  40. 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" SNLfor calculations start idle [newInput] / myCalc() Advanced EPICS Training, Dirk Zimoch 2009

  41. 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 • Compiling C code for IOCs • Using driver.makefile and require Advanced EPICS Training, Dirk Zimoch 2009

  42. The problem of compiling EPICS code • We have many different EPICS versions in use. • 3.13.2, 3.13.9, 3.13.10, 3.14.8, (3.14.10) • We have different operating systems • 2 versions of vxWorks, 3 versions of Linux, Windows • We have 2 different VME boards types in use. • MVME2300, MVME5100 • Other systems • Embedded Linux on Virtex 4, Cosylab microIOC, … • We want to run "the same" code on all systems. Advanced EPICS Training, Dirk Zimoch 2009

  43. Differences between EPICS 3.13 and 3.14 • EPICS 3.14 is designed to be independent of vxWorks • Use OSI functions instead of vxWorks functions. • Use EPICS registry instead of vxWorks symbol table. • More complex build mechanism to build "host"-IOCs. • Incompatibilities between 3.13 and 3.14 • New OSI functions are not available for 3.13. • Registry calls must be added all over the place. • for device support, sub/genSub functions, snl state machines • Makefile uses different variable names and mechanisms. (And make is a very complicated programming language.) Advanced EPICS Training, Dirk Zimoch 2009

  44. The solution: driver.makefile • It is a sophisticated Makefile that builds the same code • for all EPICS versions (3.13 and 3.14) • for all operating system versions (except Windows at the moment) • It builds a module that can be loaded with require • In many cases, it finds out what to do automatically • No need to write a complex Makefile • In special cases, it can be configured • Originally designed for drivers, device support and new record types, it can be used for sub/genSub and SNL code, too. Advanced EPICS Training, Dirk Zimoch 2009

  45. Global module Commonly used code (device driver, record type, …) Installed into global driver pool with make install Can be used by everyone Gets version number from CVS tag CVS location: G/DRV/ Can be loaded with require Local module Locally used code (SNL, sub/genSub functions, …) Installed to IOC boot directory with swit No interference with other modules Does not need version numbers CVS location: A, X, F, P Can be loaded with require The two flavours of modules Advanced EPICS Training, Dirk Zimoch 2009

  46. Example global module: hytec drivers • CVS location: G/DRV/hytec • Needs CVS tags with versions like hytec_1_4_2 • Untagged test versions are also supported. • Install to driver pool with make install • Can be loaded with require "hytec" • or require "hytec","1" • or require "hytec","1.4" • or require "hytec","1.4.2" • or require "hytec","test" Advanced EPICS Training, Dirk Zimoch 2009

  47. Example local module: genSub function • CVS location: X/ID/GAP • genSub function file: X/ID/GAP/src/e_to_gap.c • Tell project GNUmakefile to build the code in src/ ioc: build swit –Vbuild clean: make -C src $@ • Install to IOC with swit together with other project files. • Can be loaded with require "GAP" Advanced EPICS Training, Dirk Zimoch 2009

  48. Using driver.makefile • In your source directory, create a one line Makefile include /ioc/tools/driver.makefile • Running make "automagically" builds a loadable module. • Detects all .c, .cc, .C, .cpp, .st, .stt, and .dbd files in directory. • Generates module name from directory name. • Builds for all EPICS versions. • Builds only for vxWorks by default (but can build for Linux, too). • Finds dependencies on other modules (drivers). • Levels out many differences between 3.13 and 3.14 • If this is too much magic, you can configure it! Advanced EPICS Training, Dirk Zimoch 2009

  49. Configuring driver.makefile • Source code list, dbd file list • Default is all files in current directory • Overwrite:SOURCES += file1.ccSOURCES += subdir/file2.cDBDS += xxx.dbd • Why overwriting? • Files are not all in the same directory as the Makefile. • Not all files should be used. Advanced EPICS Training, Dirk Zimoch 2009

  50. Configuring driver.makefile • Project (module) name • Default name is directory name • If directory name is src or snl, default is name of parent directory • Overwrite: PROJECT = othername • Why overwriting? • I don't like directory name. • Directory name contains illegal characters like – or space. • only alphanumeric plus underscore allowed Advanced EPICS Training, Dirk Zimoch 2009

More Related