Title: Writing C code for vxWorks
1Writing C code for vxWorks
- Especially sub and genSub record
2Contents
- vxWorks intro
- Major differences between vxWorks and Unix/Linux
- vxWorks Help
- vxWorks shell
- VME access
- Calling C code from EPICS
- Subrouting records sub and genSub
- Compiling C code for vxWorks
- driver.makefile
- require
3Major differences between vxWorks and Unix/Linux
- vxWorks has no programs but many threads (called
"tasks"). - vxWorks has no main function.
- Every global (non static) function can be called
from the shell. - Every global function or variable is global to
the whole IOC. - Global variables are EVIL!
- Every function has unlimited access to every
memory location. - Every other global function and variable can be
accessed. - Every VME bus location can be accessed.
- Writing to NULL pointers corrupts the interrupt
table. - Overruning stack or allocated memory crashes the
IOC.
4vxWorks help
- Online help http//vxworks.web.psi.ch
- Important for beginnersVxWorks Programmer's
Guide, Chapter 2 - All about tasks, semaphores, watchdog timers,
interrupts - Always helpfulvxWorks Reference Manual
- All vxWorks system functions
- Run-time help Type help on the vxWorks shell.
5Accessing 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 - On Windows use hyperterm (but that is buggy).
6Calling functions from the vxWorks shell
- Don't 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 does not work.
- 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,
7Accessing VME memory
- VME memory is mapped to local address space.
- 16 kB A16
- 64 MB A24
- 1 GB A32 (of 4 GB total A32 space)
- To see mapping, type vmeMapShow
- Mapping may change between vxWorks versions or
boards. - To use mapping in C code, use sysBusToLocalAdrs.
- Writing to VME is pipelined.
- When write instruction has finished, the data has
not necessarily reached the VME board. Read back
if necessary.
8Calling C code from EPICS
- Device drivers
- Used by many record types via DTYP and INP or OUT
fields. - Some knowledge about EPICS drivers needed.
- Can only handle one record at a time.
- Subroutine records sub and genSub
- Record calls C function by name which has access
to record fields. - sub 12 double inputs, 1 double output
- genSub 21 arbitrary inputs, 21 arbitrary outputs
(including arrays) - State Notation Language
- Special C-like code for state machines
9Subroutine record sub
- 12 input links INPA INPL
- Input is copied into input fields A L (type
double) - Either use input link or write directly to input
field. - User function reads and A L and writes result
to VAL. - SNAM field contains function name.
- INAM field contains optional init function name.
- Functions have access to all record fields but
field names are lower case a l, val
10Subroutione record user function
- Inputs are in fields a l (type double).
- Output goes to val (type double).
- int foobar (struct subRecord record)
record-gtval record-gtval record-gta
record-gtb return 0 - Specify name of function in SNAM field of record.
- record (sub, "(NAME)") field (SNAM,
"foobar")
11Subroutine record initialization
- Optional init function
- int foobar_init (subRecord record)
record-gtval 1.0 return 0 - Specify init function name in INAM field.
- record (sub, "(NAME)") field (SNAM,
"foobar") field (INAM, "foobar_init") - Init function runs once at boot time.
12Advanced Asyncronous subroutine record
- If function takes long time to complete...
- run calculation in separate task.
- Setup task in init function.
- Store data for inter task communication in dpvt
field. - trigger task from record function.
- return 1 from record function.
- re-process record when calculation completes.
- Use callbackRequestProcessCallback.
- This time, pact field is 1.
- return 0 from record function.
13Asynchronous subroutine stub
- typedef struct SEM_ID trigger
CALLBACK cb / whatever you need /
asyncSubPriv - void asyncSubTask(struct subRecord record)
asyncSubPriv priv record-gtdpvt / do
calculations / callbackRequestProcessCallback
(priv-gtcb, record-gtprio, record) - int asyncSubInit(struct subRecord record)
asyncSubPriv priv malloc(sizeof(asyncSubPriv))
taskSpawn("asyncSub",200,VX_FP_TASK,10000,(FU
NCPTR)asyncSubTask,(int)record,0,0,0,0,0,0,0,0,0)
priv-gttrigger semBCreate(SEM_Q_FIFO,SEM_EMP
TY) / add error checking /
record-gtdpvt priv return 0 - int asyncSub(struct subRecord record)
asyncSubPriv priv record-gtdpvt if (priv
NULL) return ERROR if (record-gtpact)
return 0 semGive(priv-gttrigger) return
1
14General subroutine record genSub
- All inputs and outputs are arrays of user defined
type. - Input links and fields INPA INPU, A U
- Output fields and links VALA VALU, OUTA OUTU
- Input/output data type FTA FTU, FTVA FTVU
- One of CHAR, SHORT, LONG, ULONG, FLOAT, DOUBLE,
- Input/output element count NOA NOU, NOVA
NOVU - Set FT and NO fields of all used inputs and
outputs. - SNAM and INAM fields like in sub record.
- The genSub record must be loaded require
"SynApps"
15General subroutine record user function
- Input and output fields a u, vala valu are
void. - Cast to matching pointer type.
- Even if element count is 1, it is a pointer to an
array of length 1. - Check all ft and no fields in init function.
- One of menuFtypeSHORT, menuFtypeDOUBLE,
- Never access data through void before checking
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.
16The problem of compiling EPICS code
- We have 2 different vxWorks versions in use.
- The compiler runs on Linux, the code is for
vxWorks. - This is called "cross compiling".
- It is much harder to debug.
- vxWorks is similar but not exactly like
Unix/Linux. - We run EPICS on Linux (3 versions) and Windows,
too. - Windows is completely different.
- We have 4 different EPICS versions in use.
- We have 2 different VME boards types in use.
- We want to run "the same" code on all systems.
17The solution driver.makefile
- We have a sophisticated makefile that builds the
same code - for all EPICS versions
- for all operating system versions (except Windows
at the moment) - The install mechanism sorts out what code to use
for an IOC - EPICS version, OS version, board type are stored
in database.
18Using driver.makefile
- In your source directory, create a one line
Makefile - include /ioc/toos/driver.makefile
- run make
- It "automagically" detects all source code and
builds one loadable module from it for each
EPICS/OS combination. - Detects .c .cc .C .cpp .st .stt .dbd files in
directory. - It detects common module or local IOC project
modules - Common modules can be installed into our driver
pool. - Common modules need proper CVS tags with version
numbers. - If this is too much magic, it can be configured
in Makefile.
19Common modules (not only drivers)
- Are in CVS G/DRV/modulename, e.g. G/DRV/hytec
- Have CVS tags with versions like hytec_1_4_2
- Untagged test versions are also supported.
- Install to driver pool with make install
- IOC can load from driver pool with require
"hytec" - or require "hytec","1"
- or require "hytec","1.4"
- or require "hytec","1.4.2"
- or require "hytec","test"
- Recommended if code is used by many IOCs.
20Local IOC project modules
- Are in CVS in some IOC project directory,
A/DI/project/src/ - Don't need version tags.
- Can't be installed to the driver pool.
- Are installed from slsinstall together with other
project files. - Tell your project GNUmakefile to build the code
in src/ - ioc
- make -C src
- Can be loaded with require "project"
- Recommended if code is only used for one project.
- e.g. sub/genSub functions, SNL code
21Using require
- require is a SLS extension to EPICS.
- It is used in the IOC startup script.
- 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