Sunteți pe pagina 1din 58

Writing C code for IOCs

Advanced EPICS Training, Dirk Zimoch 2009

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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 dont start programs from the shell, you call functions.
When name clash happens, you might call the wrong function.
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs


XTEST-VME-ID1 > help
help
ioHelp
dbgHelp
nfsHelp
netHelp
spyHelp
timexHelp
h
i
ti
sp
taskSpawn
td
ts
tr
d
m
mRegs
pc

Print this list


Print I/O utilities help info
Print debugger help info
Print nfs help info
Print network help info
Print task histogrammer help info
Print execution timer help info
[n]
Print (or set) shell history
[task]
Summary of tasks' TCBs
task
Complete info on TCB for task
adr,args...
Spawn a task, pri=100, opt=0x19, stk=20000
name,pri,opt,stk,adr,args... Spawn a task
task
Delete a task
task
Suspend a task
task
Resume a task
[adr[,nunits[,width]]] Display memory
adr[,width]
Modify memory
[reg[,task]]
Modify a task's registers interactively
[task]
Return task's program counter

Type <CR> to continue, Q<CR> to stop:

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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;

/* internal variable */
static int card_count;

/* external variable */
int debug=0;

/* external variable */
int drvXyDebug=0;

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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
/* values for one card */
static char* addr;
static int ivec;

Right
/* linked list */
struct drvPriv {
struct drvPriv *next;
char* addr;
int ivec;
} drvPriv;
static drvPriv *first=NULL;

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs

Asynchronous subroutine stub


#include
#include
#include
#include
#include

<subRecord.h>
<callback.h>
<taskLib.h>
<semLib.h>
<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

Writing C code for IOCs

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

Writing C code for IOCs

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 */
add error messages
return 0; /* signal: done */
}

Advanced EPICS Training, Dirk Zimoch 2009

here

Writing C code for IOCs

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;
}

Advanced EPICS Training, Dirk Zimoch 2009

add error messages


here

Writing C code for IOCs

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


Advanced EPICS Training, Dirk Zimoch 2009

"SynApps"

Writing C code for IOCs

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

Writing C code for IOCs

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

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

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

the two essential


functions
device support
table name

device(waveform, CONSTANT, devIntegrateWaveform, "integrate")

record type

Link type, CONSTANT means:


"constant or link to record"

Advanced EPICS Training, Dirk Zimoch 2009

DTYP string

Writing C code for IOCs

Example soft device support: Integrate waveform


#include
#include
#include
#include
#include

<recGbl.h>
<devSup.h>
<alarm.h>
<dbAccess.h>
<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

Writing C code for IOCs

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

Writing C code for IOCs

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

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

Using SNL
start
state "cold"

[temp>25.3] /
cooling on

[temp<22] /
cooling off

state "hot"

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
}
}

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Including C-code into SNL

Escape single line with %


%...

especially #include

Escape block with %{...}

%
Avoid accessing "global" SNL
variables from within escaped
C code.

program calculator
%%#include <math.h>
%{
void myCalc(
double i1,
double i2,
double* o1,
double* o2)
{
*o1 = sin(i1 + i2);
*o2 = cos(i1 i2);
}
}%

Implementation depends on
"+r" flag
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

"Abusing" SNL
for calculations
start
idle
[newInput] / myCalc()

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
}
}

Advanced EPICS Training, Dirk Zimoch 2009

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


Using driver.makefile and require
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs

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

Writing C code for IOCs

The two flavours of modules

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

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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
or require
or require
or require

"hytec","1"
"hytec","1.4"
"hytec","1.4.2"
"hytec","test"

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

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 V
build 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

Writing C code for IOCs

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

Writing C code for IOCs

Configuring driver.makefile

Source code list, dbd file list


Default is all files in current directory
Overwrite:
SOURCES += file1.cc
SOURCES += subdir/file2.c
DBDS += 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

Writing C code for IOCs

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

Writing C code for IOCs

Configuring driver.makefile

EPICS versions to build


Default is (3.13.2), 3.13.9, 3.13.10, 3.14.8, (3.14.10)
May change over time and depends on machine
Exclude versions:
EXCLUDE_VERSIONS = 3.14
EXCLUDE_VERSIONS = 3.13.2

Why excluding versions?


Code does not compile for all EPICS versions.
Code is not necessary for all EPICS versions.

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Configuring driver.makefile

Operating system class


Default is vxWorks only (all versions)
Reason: backward compatibility with old vxWorks depended drivers.
Overwrite:
BUILDCLASSES = Linux (only for Linux)
BUILDCLASSES += Linux (for vxWorks and Linux)

Why overwriting?
Module should be used on other OS than vxWorks.
genSub or SNL code for Linux IOCs

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Configuring driver.makefile

Header file installation


Default is not to install any headers
Exception: generated header files for new record types
Overwrite:
HEADERS += file.h

Why overwriting?
Module is a lower level driver on that other drivers may depend
For example asyn or ipac
Only important for global modules
Local modules are not installed for use by others
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Make targets

make, make build


Builds a loadable module for each EPICS/OS combination

make 3.14, make 3.13.2


Builds only for certain EPICS versions

make install, make install.3.13


Installs global module to driver pool

make uninstall
Cleanly (!) removes global module from driver pool

make help
Prints some help
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Loading modules: require

require is an SLS extension.


It is used in the IOC startup script to load modules.
It checks if a module is already loaded.
Already loaded with compatible version is OK.
Already loaded with incompatible version stops booting.

It recursively solves dependencies on other modules.


It loads the library and dbd file and initializes the module.
Initialize means: make functions etc, available to the shell
Uses ld and dbLoadDatabase to load the files
Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Using require

require

"xxx"

Loads latest version of library


xxxLib, xxxLib.munch, libxxx.so, or xxx.dll
Loads dbd file xxx.dbd
3.14: Calls init function xxx_registerRecordDeviceDriver

require

"xxx","2.1"

Loads version 2.1 of library and dbd file (only for global modules)
xxxLib-2.1, xxxLib-2.1.munch, libxxx-2.1.so, or xxx-2.1.dll

require

"xxx","test"

Loads test (untagged) version of library and dbd file


Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Driver pool location

Location of global module libraries:


$INSTBASE/iocBoot/<EPICS_VERSION>/<ARCH>
/work/iocBoot/R3.13.10/T2-ppc604
/psi-xfel/prod/iocBoot/R3.14.8/SL5-x86

Location of dbd files:


$INSTBASE/iocBoot/<EPICS_VERSION>/dbd

Location of headers:
$INSTBASE/iocBoot/<EPICS_VERSION>/include

Advanced EPICS Training, Dirk Zimoch 2009

Writing C code for IOCs

Example of link system in driver pool


motorLib -> motorLib-6.2.5
motorLib-4 -> motorLib-4.963.0
motorLib-4.74 -> motorLib-4.74.1
motorLib-4.74.1
motorLib-4.96 -> motorLib-4.96.1
motorLib-4.96.1
motorLib-4.962 -> motorLib-4.962.0
motorLib-4.962.0
motorLib-4.963 -> motorLib-4.963.0
motorLib-4.963.0
motorLib-6 -> motorLib-6.2.5
motorLib-6.2 -> motorLib-6.2.5
motorLib-6.2.5
motorLib-test

Advanced EPICS Training, Dirk Zimoch 2009