Writing EPICS Drivers - PowerPoint PPT Presentation

1 / 73
About This Presentation
Title:

Writing EPICS Drivers

Description:

Writing EPICS Drivers (and Device Support) Contents Introduction A simple example driver Tips and tricks Example device support Multi-threading I/O Intr Introduction ... – PowerPoint PPT presentation

Number of Views:60
Avg rating:3.0/5.0
Slides: 74
Provided by: admin7
Learn more at: https://aps.anl.gov
Category:

less

Transcript and Presenter's Notes

Title: Writing EPICS Drivers


1
Writing EPICS Drivers
  • (and Device Support)

2
Contents
  • Introduction
  • A simple example driver
  • Tips and tricks
  • Example device support
  • Multi-threading
  • I/O Intr

3
Introduction
4
Required knowledge about EPICS
  • Records and fields
  • Standard records (ai, ao, mbbi, stringout, )
  • Probably specialized records (motor, )
  • Record processing
  • Scan methods (periodic, I/O Intr, )
  • Links that cause processing (PP links, CP links,
    forward links)
  • Channel Access clients (medm, caget, )
  • These are topics of a basic EPICS training
  • See also IOC Application Developper's Guide.

5
Required knowledge about programming
  • C
  • Variable flavours (global, local, static,
    volatile)
  • struct, union, array , typedef, enum
  • Memory allocation (malloc, calloc) and pointers
  • Pointers to functions
  • Macros (define) and conditional compilation
    (ifdef)
  • Data structures
  • Integer representations (2's complement, hex,
    byte order)
  • Bit fields (masking, shifting, )
  • Linked lists

6
Important knowledge about hardware I/O
  • I/O registers
  • Side effects of reading and writing
  • Fifo registers
  • Busses
  • VME address spaces A16, A24, A32
  • Memory mapped access
  • Out-of-order execution / pipelining
  • Interrupts
  • Interrupt levels and vectors
  • Interrupt handlers

7
Driver and Device support
8
What is an EPICS driver?
  • Software interface between EPICS records and
    hardware(or 3rd party software)
  • Usually split in 2 parts for better
    maintainability
  • Device support
  • Interfaces to records
  • Does not care about hardware
  • Files devXXX.c, XXX.dbd
  • Driver
  • Does low-level hardware (register) access
  • Does not care about records
  • Files drvXXX.c, drvXXX.h, (sometimes XXX.dbd)

9
Layers Record - Device Support Driver -
Hardware
Does not know about drivers
Record
Records
Device support function table
Record specific
s
devXXX.c
Device Support
The "glue"
Driver API functions
Driver specific
drvXXX.h
Does not know about records
Driver
drvXXX.c
Register access
Hardware specific
Hardware
10
Splitting functionality device support vs. driver
  • Device support is the "glue" between record and
    driver
  • Parses INP or OUT link
  • Reads / writes / initializes record fields
  • Calls driver API functions
  • Driver does the real work
  • Initializes the hardware
  • Creates work thread if necessary
  • Register access
  • Interrupt handling
  • Resource handling (semaphores, etc)

11
A simple example driver
12
In this chapter
  • Example hardware "myDac"
  • Interface (API) in drvMyDac.h
  • Implementation in drvMyDac.c
  • Configuration function
  • open() function
  • I/O funtions
  • Report function
  • Register to EPICS with drvMyDac.dbd
  • Registering variables
  • Registering functions

13
Example 8 channel DAC
  • The card is a VME board in A16 address space
  • One DAC "card" has 8 "signals".
  • Each signal is 16 bit (0x0000 -10 V, 0xFFFF
    10 V)
  • The 8 signals are registers at offsets 0x10,
    0x12, 0x14, 0x16
  • It is possible to read back the current register
    setting.
  • The card does not use interrupts.
  • The card has an identifier at offset 0x00
  • String "MYDAC"nullbyte

14
How to start?
  • Decide what the driver should be able to do
  • Define one function for each functionality of the
    hardware
  • Don't think about records at this time
  • Think of "cards" or "boards", "signals" and
    functionality
  • Model a "card" as a structure
  • It contains hardware register address, internal
    states, etc
  • Define a function which returns a pointer to this
    structure(like open() for a file)
  • Other functions take this "handle" as an argument
  • Define a configuration function to initialize one
    "card"

15
What to put into the header file
  • This file defines the interface to the driver
  • All public (API) driver functions
  • Maybe error codes if used as return values
  • Typedefs for used function parameters
  • This file does not contain implementation details
  • No card structure details
  • No internal functions (e.g. interrupt handler)
  • No macros etc. used only internally in driver
  • No register layout

16
Example drvMyDac.h file
ifndef drvMyDac_h define drvMyDac_h include
ltdevLib.hgt / Initialize the card (called from
startup script) / int myDacConfigure (int
cardnumber, unsigned int vmeAddress) / Define
a type for the card handle / typedef struct
myDacCard myDacCard / Get card handle from
card number / myDacCard myDacOpen (int
cardnumber) / Set and read back values / int
myDacSet (myDacCard card, int signal,
epicsUInt16 value) int myDacGet (myDacCard
card, int signal, epicsUInt16 pvalue) endif
Configuration for one card
Type definition for card handle (no details)
Get handle for card
Public driver functions What can the card do?
17
What to avoid
  • Do not use void for driver handle.
  • This allows the compiler to warn about wrong
    arguments.
  • Do not define the card handle structure in the
    header file.
  • This is an implementation detail and not of
    interest to the user.
  • Do not list internal functions in the header
    file.
  • Interrupt handlers etc. are implementation
    details.
  • Do not define the register layout in the header
    file.
  • You don't want anyone else but the driver to
    access registers.

18
Implementing the driver (part 1)
  • Define "card" structure.
  • Contains at least register base address.
  • May contain thread ID, semaphores, etc.
  • Build a container of "cards" (linked list).
  • Define macros for register access.
  • Mark all registers volatile.
  • This prevents the compiler from "optimizing" and
    reordering register access.
  • Avoid global variables whenever possible.
  • At least use static for any private global
    variable.
  • Prefix every non-static global variable with
    driver name.

19
Includes, card structure and macros in devMyDac.c
include ltstdio.hgt include ltstring.hgt include
ltdevLib.hgt include ltdrvSup.hgt include
ltepicsExport.hgt include "drvMyDac.h" / linked
list of card structures / struct myDacCard
myDacCard next int cardnumber int
vmeAddress char baseaddress static
myDacCard firstCard NULL / 8 DACs (16 bit)
at offset 0x10 and following / define
DAC(card,signal) ((volatile epicsUInt16)(card-gtba
seaddress0x10))signal / Other internally
used macros / define VME_SIZE 0x100 /
Debugging switch / int myDacDebug
0 epicsExportAddress (int, myDacDebug)
EPICS headers
Define card structure here (as linked list
element)
Make private global variables static
Macros for register access
Use compiler independent volatile type
Must be non-static and exported to EPICS. Prefix
it properly.
20
What to avoid
  • Do not use a structure to map registers.
  • Structures are compiler dependent.
  • Compiler may insert pad bytes to align elements.
  • Compiler may reorder elements to "optimize"
    access.
  • Use macros to calculate address offset.
  • Do not use compiler dependent data types for
    registers.
  • Not all compilers use 2 bytes for unsigned short.
  • Use compiler independent types like epicsUInt16
    instead.
  • Do not use global variables to store any card
    details.
  • You may have more than one card. Make a list of
    structures.

21
Implementing the driver (part 2)
  • Configuration function
  • Configure one and only one card per function
    call.
  • Give each card an individual id (number or
    string).
  • Do not use auto-numbering of cards.
  • This avoids problems when you have to remove a
    card (temporarily)
  • open() function
  • Return a card handle from a card id
  • Input/Output functions
  • Report function
  • (Interrupt handler)

22
What to do in the configuration function
  • Check parameters for sanity.
  • Calculate local (memory mapped) address of
    registers.
  • Check if the hardware is installed.
  • Create "card" structure.
  • Allocate memory.
  • Fill in values.
  • Put into linked list.
  • Give good error messages if anything goes wrong.
  • Don't forget driver name and parameters in error
    message.
  • Write error messages to stderr, not to stdout.

23
Example configuration function in drvMyDac.c
/ The configure function is called from the
startup script / int myDacConfigure (int
cardnumber, unsigned int vmeAddress) long
status volatile void baseaddress char
id6 myDacCard pcard / Check card
number for sanity / if (cardnumber lt 0)
fprintf (stderr, "myDacConfigure cardnumber d
must be gt 0\n", cardnumber) return
S_dev_noDevice / Find end of card list
and check for duplicates / for (pcard
firstCard pcard pcard(pcard)-gtnext)
if ((pcard)-gtcardnumber cardnumber)
fprintf (stderr, "myDacConfigure cardnumber d
already in use\n", cardnumber)
return S_dev_identifyOverlap
Check all parametes
Write clear error messages to stderr
Return error codes (from devLib.h or drvMyDac.h)
Check for duplicate configuration
Remember now pcard points to the end of the list
24
Configuration function continued
/ Check vmeAddress for sanity / if
(vmeAddress VME_SIZE ! 0) fprintf
(stderr, "myDacConfigure " "vmeAddress
0x04x must be a multiple of 0xx\n",
vmeAddress, VME_SIZE) return S_dev_badA16
/ Translate vme to local address and
check for overlap / status
devRegisterAddress ("myDac", atVMEA16,
vmeAddress, VME_SIZE, baseaddress) if
(status) fprintf (stderr, "myDacConfigure
" "cannot register vmeAddress 0x04x\n",
vmeAddress) return status
More parameter checks
Use devRegisterAddress (from devLib.h) to
register VME address and to translate to local
memory mapped address. This fails if the address
overlaps with another registered address.
25
Configuration function continued
Check that hardware is installed before using it.
/ Check that correct hardware is installed /
status (devReadProbe (2, (char) baseaddress,
id) devReadProbe (2, (char)
baseaddress2, id2) devReadProbe (2,
(char) baseaddress4, id4)) if (status)
fprintf (stderr, "myDacConfigure " "no
card found at vmeAddress 0x04x (localp)\n",
vmeAddress, baseaddress) return status
if (strcmp(id, "MYDAC") ! 0) fprintf
(stderr, "myDacConfigure " "card at
vmeAddress 0x04x (localp) is not a MYDAC\n",
vmeAddress, baseaddress) return
S_dev_wrongDevice
Use devReadProbe (from devLib.h) to access
hardware safely. If hardware is not installed, it
does not crash but just fails.
Check that you have really the hardware you expect
26
Configuration function finished
/ Create new card structure / pcard
malloc (sizeof (myDacCard)) if (!pcard)
fprintf (stderr, "myDacConfigure " "out
of memory\n") return S_dev_noMemory
(pcard)-gtnext NULL (pcard)-gtcardnumber
cardnumber (pcard)-gtbaseaddress
baseaddress return 0
Allocate card structure (malloc or calloc)
Remember? pcard points to end of list
Success
Fill card structure with values
27
What to avoid
  • Do not use other parameter types than int or
    char.
  • double does not work on vxWorks shell on PPC
    architecture
  • Other types are not supported by iocsh.
  • Do not use too many parameters.
  • vxWorks supports only 10 parameters for shell
    functions.
  • Do not crash if card is absent.
  • Check card before use.
  • Do not give meaningless messages like "driver
    init failed".
  • The user needs information. What failed where and
    why?
  • Provide driver/function name, failing parameter
    and error reason.

28
Hardware registers
  • A hardware register is not just a variable!
  • Write or read access may have side effects on the
    hardware.
  • Reading may get a different value than the last
    written one.
  • FIFO registers provide different values every
    time they are read, giving sequential access to
    an array through a scalar.
  • Reading or writing in pieces (e.g. low and high
    16bit word to a 32bit register) may be invalid,
    may have unexpected effects, or may require a
    certain order.
  • Always use volatile to access registers
  • This tells the compiler not to try "optimization"
    on hardware registers.

29
What to do in the API functions
  • Open
  • Check card id (number or string)
  • Find card in list of configured cards.
  • Return pointer to card structure (handle) or
    NULL.
  • I/O functions
  • Check handle for validity.
  • Read or write registers.
  • Return error code on failure or 0 on success.
  • No need to print error messages here (device
    support should do that)
  • Put in switchable debug messages.

30
Example API functions
Translate card id to handle
myDacCard myDacOpen (int cardnumber)
myDacCard card for (card firstCard card
card card-gtnext) if (card-gtcardnumber
cardnumber) return card return NULL int
myDacSet (myDacCard card, int signal,
epicsUInt16 value) if (!card) return
S_dev_noDevice if (signal lt 0 signal gt 7)
return S_dev_badSignalNumber if
(myDacDebuggt0) printf("myDacSet card d
signal d _at_p x\n", card-gtcardnumber,
signal, DAC(card, signal), (unsigned
int)value) DAC (card, signal) value
return 0
Use handle in all other functions
Check handle for NULL
Check other parameters
Return error codes on failure.
Switchable debug message
Success
Access registers
31
Example API functions continued
int myDacGet (myDacCard card, int signal,
epicsUInt16 pvalue) if (!card) return
S_dev_noDevice if (signal lt 0 signal gt 7)
return S_dev_badSignalNumber pvalue
DAC(card, signal) if (myDacDebuggt1)
printf ("myDacGet card d signal d _at_p x\n",
card-gtcardnumber, signal, DAC(card,
signal), (unsigned int)pvalue) return 0
Never forget the checks. Stability is more
important than speed.
32
What to avoid
  • Do not translate card id to structure in each API
    function call.
  • Get card handle once and use it in all other API
    functions.
  • Do not use card when configuration failed.
  • When configuration fails return NULL from open()
    call.
  • Check for NULL in all other functions.
  • Do not assume anything about records.
  • Do not use function names like write_ao().
  • The driver only cares about the features of the
    hardware.
  • Records are the business of device support.

33
Reporting hardware and driver status
  • Write a report function.
  • Print driver and register information to stdout.
  • Provide multiple levels of detail.
  • In lowest level (0) print only one line per card.
  • In higher levels print more details about
    configuration, registers, etc.
  • Register report function to EPICS.
  • Create a driver support structure.
  • Fill in a pointer to report function.
  • Export driver support structure to EPICS.

34
Example report function
long myDacReport (int detail) myDacCard
card for (card firstCard card card
card-gtnext) printf (" card d at address
p\n", card-gtcardnumber,
card-gtbaseaddress) if (detail gt 1)
int signal unsigned short value for
(signal 0 signal lt 4 signal)
value DAC (card, signal) printf("
DAC d 0x04x 8.4f V\n", signal,
value, value20.0/0xffff-10.0)
return 0 drvet myDac 2,
myDacReport, NULL epicsExportAddress
(drvet, myDac)
Print card information in any detail level
Print register contents only for higher detail
level
Driver support structure contains report function
Export driver support structure function to EPICS
35
Report function call
  • The dbior shell function calls driver report
    functions.
  • Example
  • dbiorDriver myDac card 1 _at_0xfbff1000
  • dbior "myDac",1Driver myDac card 1
    _at_0xfbff1000 DAC 0 _at_0xfbff1010 0x0000
    -10.0000 V DAC 1 _at_0xfbff1012 0x0000
    -10.0000 V DAC 2 _at_0xfbff1014 0x0000
    -10.0000 V DAC 3 _at_0xfbff1016 0x0000
    -10.0000 V DAC 4 _at_0xfbff1018 0x0000
    -10.0000 V DAC 5 _at_0xfbff101a 0x0000
    -10.0000 V DAC 6 _at_0xfbff101c 0x0000
    -10.0000 V DAC 7 _at_0xfbff101e 0x0000
    -10.0000 V

36
Exporting variables and functions to EPICS
  • VxWorks shell can access C functions and
    variables directly
  • Other architectures must run iocsh
  • Shell must know about functions, variables,
    driver support
  • Export variables and driver support from C
  • epicsExportAddress (int, myDacDebug)
  • epicsExportAddress (drvet, myDac)
  • Much more complicated for functions
  • Write parameter description
  • Write wrapper function
  • Write registrar function
  • Very ugly and error-prone

37
Wrapper and registrar for shell functions
Parameter description
include ltiocsh.hgt static const iocshArg
myDacConfigureArg0 "cardNumber", iocshArgInt
static const iocshArg myDacConfigureArg1
"vmeA16Address", iocshArgInt static const
iocshArg const myDacConfigureArgs
myDacConfigureArg0, myDacConfigureArg1 sta
tic const iocshFuncDef myDacConfigureDef
"myDacConfigure", 2, myDacConfigureArgs
static void myDacConfigureFunc (const
iocshArgBuf args) myDacConfigure
(args0.ival, args1.ival) static void
myDacRegister () iocshRegister
(myDacConfigureDef, myDacConfigureFunc) /
iocshRegister (other shell functions)
/ epicsExportRegistrar (myDacRegister)
Array of all parameters
Parameter count
for each function
Function description
Wrapper function
Unwrap parameters
Real function call
Register functions to iocsh
Export registrar to EPICS
38
Importing variables and functions to EPICS
  • Make exported C variables and functions known to
    EPICS
  • Write MyDac.dbd file with references to exported
    entities
  • Driver support structure
  • driver(myDac)
  • Variables
  • variable(myDacDebug, int)
  • Registrar
  • registrar(myDacRegister)
  • Coming soon Device support
  • device()

39
Tips and Tricks
  • From Dirk's Code Kitchen

40
A bit more safety / paranoia
  • Even if card ! NULL, it may be invalid
  • E.g. user calls myDacGet() with cardnumber
    instead of handle.
  • Accessing wrong hardware address is bad.
  • A cheap way to check the card handle is a "magic
    number"
  • Add magic number to card structure.
  • Insert magic number when card is configured.
  • Check magic number in every API function.
  • A good magic number is CRC checksum of driver
    name
  • echo -n myDac cksum2191717791 5

41
Example usage of magic numbers
define MYMAGIC 2191717791U / crc("myDac")
/ struct myDacCard epicsUInt32 magic
myDacCard next int cardnumber int
vmeAddress volatile char baseaddress int
myDacConfigure (int cardnumber, unsigned int
vmeAddress) (pcard)-gtmagic MYMAGIC
(pcard)-gtnext NULL (pcard)-gtcardnumber
cardnumber (pcard)-gtbaseaddress
baseaddress return 0 int myDacSet (myDac
card, int signal, epicsUInt16 value) if
(!card) return S_dev_noDevice if (card-gtmagic
! MYMAGIC) return S_dev_wrongDevice
Store magic number in card structure.
Initialize magic number in configure function.
Check magic number in every call.
42
Simulation Mode
  • EPICS does not support VME on Unix (or Windows)
  • devRegisterAddress() fails on softioc
  • devReadProbe() fails on softioc
  • Implement "simulation mode" on Unix for driver
    test
  • Work on allocated memory instead of
    registersifdef UNIX/ UNIX has no VME. Use a
    simulation for tests /include
    ltmalloc.hgtdefine devRegisterAddress(name, type,
    addr, size, pbase) \ (((pbase)memalign
    (0x10000, size))? \ strncpy ((void)(pbase),
    "MYDAC", size), 0 S_dev_noMemory)define
    devReadProbe (size, ptr, buff) \ (memcpy (buff,
    (void)ptr, size), 0)endif

43
Example (synchronous) device support
44
In this chapter
  • Device support structure
  • Record initialization
  • Parse INP/OUT link
  • Connect to driver
  • Fill record private data structure
  • Initialize record from hardware
  • Read or write (record processing)
  • Transfer data between record and driver
  • Linear scaling

45
How to start?
  • Decide what record types to support
  • Write one device support for each record type
  • Find out what record expects in device support
  • See record reference manual / record source code
  • Unfortunately no header file defines the device
    support
  • Choose synchronous or asynchronous support
  • If driver never blocks synchronous
  • e.g. register access
  • If driver may block or driver has callback
    functions asynchronous
  • e.g. field bus access
  • Maybe "I/O Intr" support if possible

46
The device support structure
  • One device support structure for each supported
    record type.
  • Contains pointers to device support functions.
  • Depends on record type.
  • Usuallystruct long number / of
    functions below / DEVSUPFUN report
    DEVSUPFUN init DEVSUPFUN init_record
    DEVSUPFUN get_ioint_info DEVSUPFUN
    read_or_write
  • Additional functions for some record types(see
    record reference manual)

47
Typical device support functions
  • report (can be NULL)
  • Report function similar to driver report
    function, but per record type
  • init (can be NULL)
  • Initialization of device support per record type
  • init_record
  • Initialization of device support per record
  • get_ioint_info (can be NULL)
  • For records scanned in "I/O Intr" mode
  • read or write (depending on record type)
  • Actual I/O during record processing

48
Example synchronous ao support for myDac
  • Device support structure of ao
  • Additional function special_linconv.
  • struct long number / must be
    6 / DEVSUPFUN report / can be NULL
    / DEVSUPFUN init / can be NULL /
    DEVSUPFUN init_record DEVSUPFUN
    get_ioint_info / can be NULL / DEVSUPFUN
    write DEVSUPFUN special_linconv
  • Implement 3 functions
  • long myDacInitRecordAo(aoRecord record)
  • long myDacWriteAo(aoRecord record)
  • long myDacSpecialLinconvAo(aoRecord record, int
    after)
  • Store record private data in record-gtdpvt.

49
Analog out device support (includes, private data)
include ltstdio.hgt include ltstdlib.hgt include
ltdevSup.hgt include ltrecGbl.hgt include
ltalarm.hgt include ltaoRecord.hgt include
ltepicsExport.hgt include "drvMyDac.h" typedef
struct myDacCard card int signal
myDacAoPrivate
Include header(s) of supported record type(s)
Include driver header
Store record specific data (e.g. driver handle)
in private structure
50
Analog out device support (device support
structure)
  • Implement at least 3 functions for ai/ao
  • init_record
  • read/write
  • special_linconv

long myDacInitRecordAo (aoRecord record) long
myDacWriteAo (aoRecord record) long
myDacSpecialLinconvAo (aoRecord record, int
after) struct long number
DEVSUPFUN report DEVSUPFUN init
DEVSUPFUN init_record DEVSUPFUN
get_ioint_info DEVSUPFUN write
DEVSUPFUN special_linconv myDacAo 6,
NULL, NULL, myDacInitRecordAo,
NULL, myDacWriteAo, myDacSpecialLinconvAo
epicsExportAddress(dset, myDacAo)
Put functions into device support structure.
Use NULL for unimplemented functions.
Export device support structure to EPICS.
51
Importing device support to EPICS
  • Make exported device supports known to EPICS
  • Add one line for each supported record type to
    MyDac.dbddevice (ao, VME_IO, myDacAo, "MyDac")

Name used in DTYP field of record
device support structure
link type
record type
record (ao, "(name)") field (DTYP,
"MyDac")field (OUT, "C(card) S(signal)")
52
Analog out device support (init_record part 1)
long myDacInitRecordAo (aoRecord record)
myDacAoPrivate priv epicsUInt16 initval
myDacCard card int signal if
(record-gtout.type ! VME_IO)
errlogSevPrintf (errlogFatal,
"myDacInitRecordAo s illegal OUT link type\n",
record-gtname) return -1 card
myDacOpen (record-gtout.value.vmeio.card) if
(!card) errlogSevPrintf (errlogFatal,
"myDacInitRecordAo s invalid card number d\n",
record-gtname, record-gtout.value.vmeio.card)
return S_dev_noDevice
Check INP/OUT link type.
Get link parameters
Get handle to driver
53
Analog out device support (init_record part 2)
signal record-gtout.value.vmeio.signal
if (signal lt 0 signal gt 8)
errlogSevPrintf (errlogFatal,
"myDacInitRecordAo s invalid signal number
d\n", record-gtname, signal) return
S_dev_badSignalNumber priv
(myDacAoPrivate) malloc (sizeof
(myDacAoPrivate)) if (!priv)
errlogSevPrintf (errlogFatal,
"myDacInitRecordAo s out of memory\n",
record-gtname) return S_dev_noMemory
priv-gtcard card priv-gtsignal signal
record-gtdpvt priv myDacSpecialLinconvAo
(record, TRUE) myDacGet (card, signal,
initval) record-gtrval initval return 0
Get more link parameters
Allocate private data
Fill private data and store in dpvt
Return 0 (OK) or error status
Initialize record fields
54
Analog out device support (write)
long myDacWriteAo (aoRecord record)
myDacAoPrivate priv (myDacAoPrivate)
record-gtdpvt int status if (!priv)
recGblSetSevr (record, UDF_ALARM,
INVALID_ALARM) errlogSevPrintf
(errlogFatal, "myDrvWriteAo s record not
initialized correctly\n", record-gtname)
return -1 status myDacSet (priv-gtcard,
priv-gtsignal, record-gtrval) if (status)
errlogSevPrintf (errlogFatal, "myDrvWriteAo
s myDacSet failed error code 0xx\n",
record-gtname, status) recGblSetSevr (record,
WRITE_ALARM, INVALID_ALARM) return
status
Get private data back from dpvt
Check for proper initialization
Call driver function
Return 0 or error status
55
Analog out device support (special_linconv)
long myDacSpecialLinconvAo (aoRecord record, int
after) if (after) record-gteslo
(record-gteguf - record-gtegul)/0xFFFF
record-gteoff record-gtegul return 0
Initialize linear scaling. This DAC uses range
0x0000 to 0xFFFF
  • Only required for ao and ai records
  • ao record calculates RVAL (VAL - EOFF) /
    ESLOai records calculates VAL RVAL ESLO
    EOFF
  • User provides
  • EGUL (should map to minimal raw value, e.g.
    0x0000)
  • EGUF (should map to maximal raw value, e.g.
    0xFFFF)

56
I/O Intr
57
What is I/O Intr?
  • It is a record scanning mode.
  • The record is scanned whenever the driver has new
    data.
  • Its an easy way to implement fast (gt10 Hz) or
    irregular scanning.
  • Can be triggered from driver thread or from
    interrupt level.

58
How to set up I/O Intr scanning?
  • Create one IOSCANPVT structure (from dbScan.h)
    for each source of new data events (e.g.
    interrupt) of the driver.
  • Implement get_ioint_info() in device support
  • The record calls this function whenever SCAN is
    set to I/O Intr.
  • The function should get the IOSCANPVT from the
    driver.
  • It calls scanIoInit(IOSCANPVT ) to register with
    the new data event
  • The driver calls scanIoRequest(IOSCANPVT )
    whenever it has new data.
  • The record processes and reads the value.

59
Differences to normal scanning
  • Normally the record asks the driver to start I/O.
  • Here the driver does I/O first, then processes
    the record.
  • The driver should store the data where the record
    can find it.
  • Many record can be triggered from one event
    source.
  • E.g. 32 bi records bound to an digital I/O card.
  • This may increase performance when hardware
    access is costly.

60
Asynchronous Device Support
61
About asynchronous support
  • When to use?
  • If hardware access is slow or may block.
  • Examples Fields busses (serial, GPIB, )
  • What is the problem?
  • Record processing must not block.
  • Solution Asynchronous device support
  • Driver starts a work thread that can block.
  • Device support starts driver action with
    non-blocking function.
  • Driver calls back when I/O is complete.

62
Asynchronous read or write function in detail
  • The read or write function calls driver to start
    I/O.
  • Then it sets the PACT (processing active) field
    and returns.
  • The record now knows that I/O is still in
    progress and pauses.
  • The IOC continues with other records.
  • Driver thread requests to process record again
    when ready.
  • Read or write function is called a second time
    with PACT1.
  • The function transfers values from driver to
    record and returns.
  • Record processing completes (forward link,
    monitors, etc).

63
Example asynchronous ai read function
Get private data back from dpvt
long slowDeviceReadAi (aiRecord record)
slowDeviceAiPrivate priv (slowDeviceAiPrivate)
record-gtdpvt if (!priv) / error
handling record not initialized / return
-1 if (record-gtpact 0)
driverStartRead(priv-gtcard, priv-gtsignal,
slowDeviceFinishedAi, record) record-gtpact
1 return 0 if (priv-gtstatus ! 0)
errlogSevPrintf(errlogFatal,
"myDriver s driver read failed error code
0xx\n", record-gtname, priv-gtstatus)
recGblSetSevr(record, READ_ALARM,
INVALID_ALARM) return priv-gtstatus
record-gtrval priv-gtvalue return 0
Driver will call back when finished
Call non-blocking driver function to start I/O
first call
Tell record to wait and return
second call
Handle error status of driver
Get data from driver(see next slide)
64
Asynchronous finish function
void slowDeviceFinished (dbCommon record, int
status, epicsInt16 value) slowDevicePrivate
priv (slowDevicePrivate) record-gtdpvt
private-gtstatus status private-gtvalue
value callbackRequestProcessCallback
(priv-gtcb, record-gtprio, record)
Store value and result where record can find it.
Request record processing in one of the three
callback threads.
65
Multi-threading issues
66
Threads used in EPICS
  • Many parts of the EPICS software work in
    parallel.(In vxWorks, threads are called tasks.)
  • E.g. each SCAN type runs in a separate thread
  • High priority thread for ".1 second" scanning
  • Low priority thread for "10 second" scanning
  • Lowest priority for "Passive" scanning as the
    result of a caput.
  • Additional threads for callbacks, timeouts,
    channel access,
  • Many threads may execute the same function at the
    same time
  • E.g. two records with the same driver and
    different scan rates.
  • The CPU can switch from one thread to another at
    any time.

67
The re-entrancy problem
  • Functions must be re-entrant.
  • Bad examplechar numToString (int number)
    static char buffer20 sprintf (buffer, "d",
    number) return buffer
  • What's bad?
  • Thread 1 calls numToString(12345).
  • Some time after sprintf() the CPU switches to
    thread 2.
  • Thread 2 calls numToString(42).
  • Some time after sprintf() the CPU switches to
    thread 1.
  • Thread 1 uses the function result and reads "42"

68
Making code re-entrant
  • Never return a pointer to static memory.
  • Never call such a function.
  • Nobody would do that? System functions that do
  • char ether_ntoa (const struct ether_addr addr)
  • char asctime (const struct tm tm)
  • struct tm localtime (const time_t timep)
  • char strerror (int errnum)
  • Use functions where the caller provides the
    buffer
  • char ether_ntoa_r (const struct ether_addr
    addr, char buf)
  • char asctime_r (const struct tm tm, char buf)
  • struct tm localtime_r (const time_t timep,
    struct tm result)
  • int strerror_r (int errnum, char buf, size_t n)

69
Non-atomic operations on global resources
  • Non-atomic operations may be interrupted by an
    other thread.
  • If the other thread accesses the same global
    resource it may get inconsistent data.
  • Example

70
What are global resources?
  • Global variables
  • Static variables
  • Heap objects
  • Hardware registers
  • Files
  • Directories
  • Sockets
  • Anything for that you have only a pointer or
    handle

71
What are non-atomic operations?
  • read-modify-write
  • if (p NULL) p
  • if (! file_exists(filename)) fopen (filename,
    "w")
  • flags 1
  • reg mask
  • counter
  • sequential read/write
  • strcpy (globalstring, s)
  • globalstruct.a a globalstruct.b b
  • addressregister adr val valueregister
  • element-gtnext previous-gtnext previous-gtnext
    element

72
What is thread-safe?
  • Local variables
  • Everything on the stack is thread specific.
  • errno
  • Even though it looks like a global variable, it
    is thread specific.
  • Single threaded context
  • startup script
  • functions called from iocInit
  • driver and record initialization
  • Resources used only by one thread
  • Resources used only in interrupt handler

73
How to make non-atomic operations safe?
  • Disable interrupts.
  • Only interrupts can cause unexpected thread
    switch.
  • This is very brute.
  • Do this only for VERY SHORT times.
  • Use mutual exclusion semaphores.
  • Use operating system independent wrapper
    (epicsMutex).
  • Be careful to prevent deadlocksTwo different
    threads must never take two different semaphores
    in reverse order.
  • Lock resources as short as possible.
Write a Comment
User Comments (0)
About PowerShow.com