Daphne User-Functions Page 1 User-Functions 18 July 1991 1 User-Functions The main purpose of the user-function is to compute pseudo-parameters. If you are not computing any pseudo-parameters there is usually no need to write a user-function: instead the user should load the "standard" event processor program (Section 27.3 of EG). A second reason to write a user function is to modify an event before it is written to tape. The organization of a user-function is essentially the same for acquisition, replay on the Event Processors, or replay on the VAX. A user-function written for acquisition can usually be used without changes for replay on the VAX or replay on the Event Processors. The converse is not true, however, since VAX user-functions tend to require access to files containing information such as calibration constants: it is not possible to access VAX files from the Event Processors. A description of the difference between the VAX/VMS Fortran compiler and the National Semiconductor GNX cross compiler is given in section 26.2 of EG A Daphne user function consists of several subroutines and functions, all of which are optional. These routines are: - An initialization routine for when sorting starts (Entry userIn) - A routine for when a FIX or FLT variable changes (Entry FixFlt) - Up to 50 routines to be executed as part of "Condition" evaluation (Entry points user1, user2, ... user50) - A routine for when sorting stops (Entry userEx) Some examples of simple user functions begin at section 1.19. Daphne User-Functions Page 2 User-Functions 18 July 1991 1.1 Multiple Entry Points The user can code the parts of a Daphne user-function as separate Fortran subroutines and functions, or as several entry points of a single Fortran module. The use of multiple entry points in a single module is recommended for the following reasons: - Variables need to be declared only once - Declarations of variables shared among the user routines will always match as there is only a single declaration - It will be easier to locate the source of an arithmetic trap when working from the link map, since there will be a single base address for all user routines. The main reasons NOT to use multiple entry points are (a) if the subroutines or functions must call one another either directly or indirectly or (b) the data types of the functions are inconsistent. For instance, one cannot have one entry point of a module which is INTEGER*4 and another which is REAL*4. Fortunately, there is no reason not to declare all Daphne user function entry points as LOGICAL or LOGICAL*4. Although all entry points of a function must be of the same type, the arguments of these functions may differ in number and order. The next page shows how a collection of several subroutines might be recast into a single module with multiple entry points. Daphne User-Functions Page 3 User-Functions 18 July 1991 Separate Subprograms Single Subprogram with Entry Statements +------------------------------+ +------------------------------+ | subroutine a (aArg,bArg) | | function a (aArg,bArg) | | integer*4 aArg,bArg | | logical a,b,c | | common /c/ x,y,z | | integer*4 aArg,bArg | | ... | | real*4 cArg | | return | | common /c/ x,y,z | +------------------------------+ | c | | ... | +------------------------------+ | return | | logical function b (aArg) | | c | | integer*4 aArg | | entry b (aArg) | | common /c/ x,y,z | | ... | | ... | | b=... | | b=... | | return | | return | | c | | end | | entry c (cArg) | +------------------------------+ | ... | | c=... | +------------------------------+ | return | | logical function c (cArg) | | end | | real*4 cArg | +------------------------------+ | common /c/ x,y,z | | ... | | c=... | | return | | end | +------------------------------+ Illegal use of ENTRY (Routine A calls B, another entry point of the same module) +------------------------------+ | subroutine a (aArg) | | integer*4 aArg,bArg | | c | | ... | | call b (xyz) | | return | | c | | entry b (bArg) | | ... | | return | | end | +------------------------------+ Daphne User-Functions Page 4 User-Functions 18 July 1991 1.2 Service Routines The Daphne developers have written some subroutines which users might find handy in writing their user functions: - bit16 - break apart an INTEGER*2 variable into separate bits - cntbit - count number of bits set in an INTEGER*2 variable - defPEV - define pseudo-event vector - defFIX - define FIX parameter vector - defFLT - define FLT parameter vector - trapID - aid to locating source of arithmetic traps - unpack - break apart event using pattern words - unpack_hit - break apart event using pattern words and provide extra information for hit patterns - zeroI2 - zero an INTEGER*2 array These routines are called "service routines", and are described starting at section 1.9. 1.3 Interception of Arithmetic Traps Both the VAX and NS3200 sorting programs intercept arithmetic traps which occur in user functions. In the VAX version the user can replace the Daphne supplied trap handler with one of his own. The NS32000 sort program does not allow the user to substitute a trap routine for the standard one. Arithmetic traps in the user functions are not considered fatal since the problem may by data dependent, and it might be possible to process most events. For instance, a division by zero might occur only in 1 out of 1,000,000 events in which two ADCs both read zero. In contrast, arithmetic traps in the userIn, userEx, or FixFlt routines are fatal: sorting will not continue. This is because these three routines do not depend on event data. They should give the same result, regardless of which event was last analyzed. For help in diagnosis of arithmetic traps on the VAX see the Replay Guide, a separate document, which contains instructions on how to use the VAX/VMS symbolic debugger with Daphne user-functions. To diagnose arithmetic traps on the NS3200 see section 2. Daphne User-Functions Page 5 User-Functions 18 July 1991 1.4 Entry UserIn (User Initialization) This routine is called at the beginning of the sort process to let the user perform any user specific or run specific initialization. The userIn routine almost always includes a call to subroutine defPEV (Section 1.10) to define the pseudo-event vector The declaration of userIn: +-----------------------------------------------------------------+ | Event Processors Only | +-----------------------------------------------------------------+ | entry userIn | | | | No Arguments for Event Processor | +-----------------------------------------------------------------+ +-----------------------------------------------------------------+ | VAX Replay Only | +-----------------------------------------------------------------+ | entry userIn (runNumberString) | | | | runNumberString - input/character*(*) | | the run number entered by the user | +-----------------------------------------------------------------+ Daphne User-Functions Page 6 User-Functions 18 July 1991 1.5 Entry userEx (User Exit) This optional VAX routine is called at the end of the sorting to let the user perform any user or run specific clean up. For example, the user could write summary information to a log file, or print out performance statistics. The declaration of userEx: +-----------------------------------------------------------------+ | VAX Replay Only | +-----------------------------------------------------------------+ | entry userEx (statusCode) | | | | statusCode - input/integer*4 | | | | a standard VMS status code giving | | exit status of the sort process | | if odd then normal exit from sort | | if even then exit is due to error | +-----------------------------------------------------------------+ There is no support for a user exit routine on the Event Processors. Daphne User-Functions Page 7 User-Functions 18 July 1991 1.6 Entry User (Process an Event) This routine is optional, but almost always supplied. It is called once for each event before any maps, windows, conditions, linearizations, etc. are evaluated. It allows the user to compute pseudo-parameters for the pseudo-event vector declared in the call to defPEV by userIn (Section 1.10). The declaration of User: +------------------------------------------------------------------+ | entry user (eventType,eventSize,eventVector) | | | | eventType - input/integer*2 | | the type of the event | | for experiments with: | | | | fixed length events: always 0 | | | | variable length events with a single | | event type: always 0 | | | | multiple event types: the event type | | assigned by the user's event handler | | program (a number between 0 and 15) | | | | eventSize - input/integer*2 | | the number of integer*2 words in the event | | | | eventVector - input/integer*2 array | | the event data | +------------------------------------------------------------------+ Daphne User-Functions Page 8 User-Functions 18 July 1991 1.7 Logical Function User1, User2, ... User50 These optional routines allow the user to break the computation of pseudo parameters into as many as 50 parts. This is useful for two main reasons: - Linearization values are sometimes needed to compute pseudo-parameters. If you want to use linearizations in computing pseudo-parameters then place the calculation in a USER-n routine and precede the USER-n condition by a FORCELNZ condition. - An expensive computation is needed for only a fraction of events. Place the computation in a USER-n function after a KILL condition which aborts processing of events which are not of interest. See section 35.8 of EG for an example. The declaration of User1, User2, ... User50: +------------------------------------------------------------------+ | logical function user1 (eventType,eventSize,eventVector, | | lnzVector) | | | | function value - output/logical | | | | the function value used to set the | | corresponding condition to true (or false) | | | | eventType - input/integer*2 | | the type of the event | | same meaning as for the plain USER function | | | | eventSize - input/integer*2 | | the number of integer*2 words in the event | | | | eventVector - input/integer*2 array | | the event data | | | | lnzVector - input/integer*4 array | | | | value of linearization 1 in lnzVector(1) | | linearization 2 in lnzVector(2) | | and so on | | | | the user must be sure that the linearization | | has been evaluated for this event, | | otherwise the array will contain old | | values from previous events | | | | *** WARNING *** the lnzVector is integer*4 | +------------------------------------------------------------------+ Daphne User-Functions Page 9 User-Functions 18 July 1991 1.8 Entry FixFlt (Change in Fix or Float Values) This routine is optional. It is called each time the user changes a FIX or FLT parameter. It is also called as part of the sort initialization sequence, after entry point userIn has been called, but before any events are sorted. There are two major uses for the FixFlt entry point: - Derived Values If computations depend on a complex expression, which in turn depends on parameters which are changed only by the $FIX and $FLT command, the complex expression can be computed just once in the FixFlt routine rather than recomputed for each event. A simple case would be a change in units from angle-in-degrees to angle-in-radians. A more complicated case would be a quanity which depends on the arctangent of the quotient of two quantities. - Request Program Action The user can change the value of a $FIX or $FLT parameter to get the attention of the program. For instance, by changing the value of some $FIX parameter during replay on the VAX from 0 to 1, the program would enter the FixFlt routine where the program might recognize that a value of 1 for that parameter means the program should print out some summary information, or enter into a dialogue, etc. The declaration of FixFlt: +------------------------------------------------------------------+ | entry FixFlt | | | | This entry point has no arguments | | | +------------------------------------------------------------------+ See sections 1.11 and 1.12 on defFIX and defFLT for how to declare and access the FIX and FLT variables. Daphne User-Functions Page 10 User-Functions 18 July 1991 1.9 Subroutine TRAP (Interception of Arithmetic Traps on the VAX) Although the user has the option of supplying a handler for arithmetic traps during replay on the VAX, the Daphne routine is generally adequate when used in combination with the VAX/VMS symbolic debugger. The Daphne supplied TRAP routine counts the number of arithmetic traps and prints a message on the command terminal when the number of traps is a power of 10 or a multiple of 100,000. The declaration of the VAX version of TRAP: +------------------------------------------------------------------+ | VAX Replay Only | +------------------------------------------------------------------+ | subroutine trap (eventType,eventLength,event,trapCode,trapID) | | | | eventType - input/integer*2 | | the type of the event | | | | same meaning as for the plain USER function | | | | eventSize - input/integer*2 | | the number of integer*2 words in the event | | | | eventVector - input/integer*2 array | | event being analyzed at time of trap | | this is the event as written to tape | | | | trapCode - input/integer*4 | | standard VMS status code: | | | | SS$_INTOVF Integer overflow | | SS$_INTDIV Integer division by 0 | | | | SS$_FLTOVF Floating point overflow | | SS$_FLTOVF_F | | | | SS$_FLTDIV Floating point division by 0.0 | | SS$_FLTDIV_F | | | | SS$_FLTUND Floating point underflow | | SS$_FLTUND_F | | | | to define the SS$_xxx symbols use the Fortran | | statement: | | | | INCLUDE '($SSDEF)' | | | | trapID - input/integer*4 | | last value registered by a call to | | trapID(number) | +------------------------------------------------------------------+ Daphne User-Functions Page 11 User-Functions 18 July 1991 1.10 Service Routine defPEV (Define Pseudo-Event Vector) This routine is called by userIn to tell the sort program the location and length of the pseudo-event vector. This call is required if the user-function uses pseudo-parameters. +-----------------------------------------------------------------+ | entry userIn (runNumberString) | | .... | | integer*2 pseudo (50) | | ... | | call defPEV (pseudo,pseudo(50)) | | ^ ^ | | | | | | | +-- last element of | | | pseudo-event vector | | | | | +-- first element of pseudo-event vector | | ... | +-----------------------------------------------------------------+ Daphne User-Functions Page 12 User-Functions 18 July 1991 1.11 Service Routine defFIX (Define FIX Vector) This routine is called by userIn to tell the sort program the location and length of the FIX parameter vector. This call is required if the user-function uses FIX parameters. A user defines the names of the FIX variables with the $FIX/DEFINE command (Section 22 of EG). Normally, the last step is to create a Fortran compatible INCLUDE file which declares all the FIX variables by name and creates Fortran EQUIVALENCE statements between them and the FIX vector. For example: +-----------------------------------------------------+ | $ FIX/DEFINE MASSLO 40 | | $ FIX/DEFINE MASSHI 60 | | $ FIX/LIST/FORTRAN/OUTPUT=FIXDEFS.INC | +-----------------------------------------------------+ Would create the INCLUDE file: +-----------------------------------------------------+ | c | | c Date: 5-OCT-87 23:21:08 | | c | | common /fixcom/ fixArr,fixZZZ | | integer*4 fixArr ( 100) | | integer*4 fixZZZ | | c | | integer*4 MASSLO | | equivalence (fixarr( 1),MASSLO) | | integer*4 MASSHI | | equivalence (fixarr( 2),MASSHI) | +-----------------------------------------------------+ The include file allows the user to reference the FIX parameters by name ("MASSLO") and creates a symbolic name for the last element of the FIX vector ("FIXZZZ"). The call to defFIX is now routine: +--------------------------------------------------------------+ | entry userIn (runNumberString) | | .... | | include 'FIXDEFS.INC' | | ... | | call defFIX (fixArr,fixZZZ) | | ^ ^ | | | | | | | +-- last element of FIX vector | | +-- first element of FIX vector | | ... | | entry user (eventType,nWords,eventVector) | | if (mass .ge. massLo .and. mass .le. massHi) then | | ... | | else | | ... | | endif | +--------------------------------------------------------------+ Daphne User-Functions Page 13 User-Functions 18 July 1991 1.12 Service Routine defFLT (Define FLT Vector) The defFLT routine is essentially the same as defFIX, except that $FLT and defFLT are used with Fortran REAL*4 variables, whereas $FIX and defFIX are used with Fortran INTEGER*4 variables. The defFLT routine is called by userIn to tell the sort program the location and length of the FLT parameter vector. This call is required if the user-function uses FLT parameters. A user defines the names of the FLT variables with the $FLT/DEFINE command (Section 23 of EG). Normally, the last step is to create a Fortran compatible INCLUDE file, which declares all the FLT variables by name, and creates Fortran EQUIVALENCE statements between them and the FLT vector. For example: +-----------------------------------------------------+ | $ FLT/DEFINE T0 0.378 | | $ FLT/DEFINE U 60.0E3 | | $ FLT/LIST/FORTRAN/OUTPUT=FLTDEFS.INC | +-----------------------------------------------------+ Would create the INCLUDE file: +-----------------------------------------------------+ | c | | c Run Information | | c | | common /fltcom/ fltArr,fltZZZ | | integer*4 fltArr ( 100) | | integer*4 fltZZZ | | c | | real*4 T0 | | equivalence (fltarr( 1),T0) | | real*4 U | | equivalence (fltarr( 2),U) | +-----------------------------------------------------+ The include file allows the user to reference the FLT parameters by name ("T0") and creates a symbolic name for the last element of the FLT vector ("FLTZZZ"). The call to defFLT is now routine: +--------------------------------------------------------------+ | subroutine userIn (runNumberString) | | .... | | include 'FLTDEFS.INC' | | ... | | call defFLT (fltArr,fltZZZ) | | ^ ^ | | | | | | | +-- last element of FLT vector | | +-- first element of FLT vector | | ... | | entry user (eventType,nWords,eventVector) | | if (time .lt. t0) ... | | ... | +--------------------------------------------------------------+ Daphne User-Functions Page 14 User-Functions 18 July 1991 1.13 Service Routine trapID (Debug Aid for Arithmetic Traps) The trapID routine is used to help identify the section of code causing arithmetic traps on an Event Processor. Suspected parts of a program are sandwiched between calls to the trapID routine: call trapID (10) call trapID (11) call trapID (12) etc. Should an arithmetic trap be intercepted, the trap recovery routine passes the last trap ID number (in the example above: 10, 11, or 12) to the host to be included in the error message, thus identifying the block of code containing the problem. See section 2 for information on how to read a report of an arithmetic trap on an Event Processor. The trapID subroutine is implemented on the VAX for compatibility with Event Processor user functions, however, it is not recommended for the VAX because the VAX/VMS symbolic debugger provides a much better and more efficient tool for this class of problem. +-----------------------------------------------------------------+ | Recommended for Event Processors Only | +-----------------------------------------------------------------+ | subroutine trapID (idWord) | | | | idWord - input/integer*2 | | word to be reported to host computer when | | there is an arithmetic trap | +-----------------------------------------------------------------+ Daphne User-Functions Page 15 User-Functions 18 July 1991 1.14 Service Routine zeroI2 (Zero an INTEGER*2 Array) The zeroI2 subroutine is an efficient way to zero an entire INTEGER*2 array. +------------------------------------------------------------------+ | zeroI2 recommended for Event Processors | +------------------------------------------------------------------+ | subroutine zeroI2 (nWords,vector) | | | | nWords - input/integer*2 | | the number of elements in the vector | | | | vector - output/integer*2 array of size "nWords" | | the entire array is filled with zero | +------------------------------------------------------------------+ Although the subroutine is supported on both the VAX and the NS32000, it is of less importance on the VAX, since the VAX Fortran compiler can recognize such a loop, when optimization is enabled, and use the appropriate machine instruction. The NS32000 does not generate very good machine code to zero an entire array. The main use of the subroutine is to zero the pseudo-event vector at the beginning of the user function. For instance, it could replace the "DO loop" in the following code fragment: +-----------------------------------------------------------------+ | ... | | integer*2 npev | | parameter (nPev=300) | | integer*2 pev (nPev) | | ... | | do 10 i=1,nPev -----> | | pev(i)=0 -----> call zeroI2 (nPev,pev) | | 10 continue -----> | | ... | +-----------------------------------------------------------------+ Daphne User-Functions Page 16 User-Functions 18 July 1991 1.15 Service Routine BIT16 (Unpack Bits of an INTEGER*2 Word) The BIT16 routine makes it easy to test individual bits of a 16 bit INTEGER*2 variable by copying the "i th" bit of the word to the "i th" element of a 16 element INTEGER*2 array. For example, if bit 6 of the variable is 1 then element 6 of the array is set to 1 otherwise it is set to 0. +------------------------------------------------------------------+ | subroutine BIT16 (anInt,array) | | | | anInt - input/integer*2 | | variable to be broken apart into separate bits | | | | array - output/integer*2 array of 16 elements | | | | bit 1 of "anInt" 1 then arrary(1) set to 1 | | otherwise set to 0 | | bit 2 of "anInt" 1 then arrary(2) set to 1 | | otherwise set to 0 | | ... | | bit 16 of "anInt" 1 then arrary(16) set to 1 | | otherwise set to 0 | | | | the least significant bit of "anInt" is bit 1 | | the most significant bit of "anInt" is bit 16 | +------------------------------------------------------------------+ This provides a method, independent of the compiler, of testing individual bits of a variable. The VAX and NS32000 have different names for bit manipulation routines (Section 26.2 of EG). Daphne User-Functions Page 17 User-Functions 18 July 1991 1.16 Service Routine CNTBIT (Count Bits Set in an INTEGER*2 Word) The CNTBIT routine provides a fast, compiler independent method of counting the number of bits set in a word. +------------------------------------------------------------------+ | integer*4 function CNTBIT (anInt) | | | | function value - output/integer*4 | | number of bits set in word (between 0 and 16) | | | | anInt - input/integer*2 | | count the number of bits that are "1" | | | | Examples: | | cntbit(0) is 0 | | cntbit(255) is 8 | | cntbit(256) is 1 | | cntbit(-1) is 16 | +------------------------------------------------------------------+ Daphne User-Functions Page 18 User-Functions 18 July 1991 1.17 Service Routine UNPACK This routine is for unpacking a variable length event composed of multiple, optional, segments. A segment may be one or more 16 bit words. It is assumed that bits in a pattern word near the beginning of the event will be set or cleared to indicate the presence (or absence) of segments. Each pattern word is 16 bits, although not necessarily all bits of each pattern word are used. There may be more than one pattern word. This routine is controlled by a description passed to the routine as an array. For each possible segment there is a row in the array describing how to test for the presence of the segment (what bit in what word of the event vector to test) and where to put the segment, if it is present (how many words and what position of the pseudo-event vector). On exit, the subroutine returns to its caller the number of words in the event, and a vector describing which fields were found to be present. Fields NOT present are NOT zeroed in the pseudo-event vector. Daphne User-Functions Page 19 User-Functions 18 July 1991 Suppose one has an event with two pattern words: 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Pattern | | 3 | 3 | 1 | Word | | | | | 1 | | B2| B1| A1| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Pattern | | 4 | 4 | 4 | | 2 | 2 | 2 | Word | | | | | | | | | 2 | | D3| D2| D1| | C3| C2| C1| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ The numbers at the top of the boxes indicate the number of parameters for that segment or detector. The two character symbols at the bottom of each box is a name for the detector or segment assigned to that bit of the pattern word. If the box is empty, the bit is not used. Assume that the segements are assigned to positions in the pseudo-event vector in the following way: Name Number of Parameters Position in Pseudo-event Vector A1 1 1 B1 3 11,12,13 B2 3 14,15,16 C1 2 21,22 C2 2 23,24 C3 2 25,26 D1 4 31,32,33,34 D2 4 41,42,43,44 D3 4 51,52,53,54 The UNPACK routine is designed to do this unpacking from the real event vector to the pseudo-event vector. The most complicated part of using the routine is describing the relationship between bits in the pattern word, detectors, and positions in the pseudo-event vector. This information is supplied to the subroutine in the form of a table. The first page following gives the calling sequence for the UNPACK routine. The second page following shows how the multiple detector example given above would be represented using the table that the UNPACK routine requires. Daphne User-Functions Page 20 User-Functions 18 July 1991 +----------------------------------------------------------------------+ | subroutine unpack (desc,ev,evHead,evEnd,pev,bitVal) | | | | desc - input/integer*2 array of size (5,*) | | describes how to unpack | | the event vector into the pseudo-event vector | | | | desc (1,i) word # in the event vector of the pattern | | register for the "i th" field | | event vector begins at word 1 | | | | desc (2,i) bit # in pattern register of "i th" field | | starts with bit 1 | | must be between 1 and 16 | | | | desc (3,i) number of words to copy from event vector | | to pseudo-event vector when bit is set | | | | desc (4,i) offset (in words) in pseudo-event vector of | | position to place parameters | | pseudo-event vector begins at word 1 | | | | desc (5,i) which element of the "bitVal" vector should | | be set to 1 (or zero) to indicate that an | | optional segment is present (or absent) | | | | list is terminated by negative number in position (1,i) | | | | ev - input/integer*2 vector of size (*) | | event vector | | | | evHead - input/integer*2 | | the number of "header" words before the beginning of the | | first data word (non-pattern word) to be unpacked | | Example: | | if there are two pattern words then evHead=2 | | | | evEnd - output/integer*2 | | the position in the event vector of the last word unpacked | | Example: | | if an event vector consists of 2 pattern words and | | 14 data words (non-pattern words) then evHead=2 and | | evEnd will be set to 16 on exit from the routine | | | | this is especially important when there is more than one | | group of pattern words, and some pattern words must be | | analyzed before the location of the other groups of | | pattern words can be located in the event | | | | pev - output/integer*2 vector of size (*) | | pseudo-event vector | | | | bitVal output/integer*2 vector of size (*) | | records the value of the bit described by a row of the | | "desc" array were | | if field described by desc (*,i) is present then | | "bitVal (desc(5,i))" is set to 1 otherwise set to 0 | +----------------------------------------------------------------------+ Daphne User-Functions Page 21 User-Functions 18 July 1991 Note, in particular, that this is an INTEGER*2 array and that the last row of the table contains values of (-1) to indicate to the unpack subroutine that this is the last row. +-----------------------------------------------------------------+ | integer*2 desc (5,10) | | | |c pat bit nwords pseudo bitVal | | | | data desc / 1, 1, 1, 1, 1, | | * 1, 2, 3, 11, 2, | | * 1, 3, 3, 14, 3, | | * 2, 1, 2, 21, 4, | | * 2, 2, 2, 23, 5, | | * 2, 3, 2, 25, 6, | | * 2, 10, 4, 31, 7, | | * 2, 11, 4, 41, 8, | | * 2, 12, 4, 51, 9/ | | * -1, -1, -1, -1, -1/ | | | +-----------------------------------------------------------------+ Since standard Fortran allows only 19 continuation lines, you may want to use the following style of data declaration when there are more than 19 segments to be defined: +-----------------------------------------------------------------+ | integer*2 desc (5,10) | | | |c pat bit nwords pseudo bitVal | | | | data (desc(i, 1),i=1,5) / 1, 1, 1, 1, 1 / | | data (desc(i, 2),i=1,5) / 1, 2, 3, 11, 2 / | | data (desc(i, 3),i=1,5) / 1, 3, 3, 14, 3 / | | data (desc(i, 4),i=1,5) / 2, 1, 2, 21, 4 / | | data (desc(i, 5),i=1,5) / 2, 2, 2, 23, 5 / | | data (desc(i, 6),i=1,5) / 2, 3, 2, 25, 6 / | | data (desc(i, 7),i=1,5) / 2, 10, 4, 31, 7 / | | data (desc(i, 8),i=1,5) / 2, 11, 4, 41, 8 / | | data (desc(i, 9),i=1,5) / 2, 12, 4, 51, 9 / | | data (desc(i,10),i=1,5) / -1, -1, -1, -1, -1 / | +-----------------------------------------------------------------+ If you still find this confusing, you may want to look at the source code for the UNPACK routine. The VAX source code is in DAPTAB:VAXUNPACK.FOR The NS32000 source code is in DAPEP:NSUNPACK.FOR Daphne User-Functions Page 22 User-Functions 18 July 1991 1.18 Service Routine UNPACK_HIT UNPACK_HIT is a version of UNPACK which provides extra information when unpacking an event. One use of the routine is to produce histograms of bit patterns. For UNPACK_HIT the description array created by the user is six elements wide rather than five (as with UNPACK). When an optional segment is present in an event, the sixth element of the description array is copied to the corresponding element of the "bitVal" array. In contrast, the plain UNPACK routine would store the value 1 in the "bitVal" array. The value in the sixth element of the description array is the extra information some users need to simply the construction of histograms of bit patterns. When an optional segment is not present in an event both routines store 0. Users who wish to create histograms of detectors which have fired, will place in the sixth element of the row, the detector number corresponding to the bit in the pattern word being tested. A histogram of the appropriate section of the "bitVal" vector will yield a list of detector numbers that fired (or zero if the detector did not fire). By combining this with the /NOZERO qualifier one has an accurate picture of how many times each detector has fired. To examine the source code for the UNPACK_HIT routine: The VAX source code is in DAPTAB:VAXUNPACK_HIT.FOR The NS32000 source code is in DAPEP:NSUNPACK_HIT.FOR Daphne User-Functions Page 23 User-Functions 18 July 1991 1.19 Examples 1.19.1 Example 1 - Minimal User Function +------------------------------------------------------------------+ | logical function userIn | | implicit none | | c | | c Initialization Routine | | c Called just once at the beginning of a run | | c Function value is ignored | | c | | c et - (event type) is hardly ever used | | c np - (number of parameters) is hardly ever used | | c ev - the real-event vector is composed of 16 bit integers | | c | | integer*2 et | | integer*2 np | | integer*2 ev (*) | | c | | c the pseudo-event vector is composed of 16 bit integers | | c there is no intrinsic limit to the number of pseudo-parameters| | c in this example the user needs only 30 pseudo-parameters | | c | | integer*2 pev (30) | | c | | c must declare entry point & functions as logicals | | c | | logical user | | c | | c tell the sort program where pseudo-events are located | | c | | call defPEV (pev,pev(30)) | | c | | c end of initialization | | c the function value returned by userIn is ignored | | c | | return | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | | entry user (et,np,ev) | | c | | c called once for each event before any maps, conditions, | | c windows, linearizations, etc. are evaluated | | c it can compute pseudo-parameters | | c | | c only argument is the real event (composed of 16 bit integers) | | c | | pev(1)=(ev(1)+ev(2))/2 | | pev(2)=(ev(1)-ev(2)) | | | | | | | | return | | end | +------------------------------------------------------------------+ Daphne User-Functions Page 24 User-Functions 18 July 1991 1.20 Example 2 - USERIN USER and USER1 +------------------------------------------------------------------+ | logical function userIn | | implicit none | | c | | c Initialization Routine | | c Called just once at the beginning of a run | | c Called with single argument giving name of run as string | | c Function value is ignored | | c | | c et - (event type) is hardly ever used | | c np - (number of parameters) is hardly ever used | | c ev - the real-event vector is composed of 16 bit integers | | c | | integer*2 et | | integer*2 np | | integer*2 ev (*) | | c | | c the pseudo-event vector is composed of 16 bit integers | | c there is no intrinsic limit to the number of pseudo-parameters| | c in this example the user needs only 30 pseudo-parameters | | c | | integer*2 pev (30) | | c | | c linearization values can be used in USER1 USER2 ... USER50 | | c routines, but only if the user can be sure they have been | | c evaluated - see the main text for more information | | c | | c note that the linearization vector is composed of 32 bit | | c integers - unlike the event and pseudo-event vectors | | c | | integer*4 lnzVec (*) | | c | | c must declare entry point & functions as logicals | | c | | logical user,user1 | | c | | c tell the location of the pseudo-parameters | | c | | call defPEV (pev(1),pev(30)) | | c | | c end of initialization | | c the function value returned by userIn is ignored by VAXSORT | | c | | return | | c | | | Daphne User-Functions Page 25 User-Functions 18 July 1991 | | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | | c | | entry user (et,np,ev) | | c | | c called once for each event before any maps, conditions, | | c windows, linearizations, etc. are evaluated | | c it can compute pseudo-parameters | | c | | c only argument is the real event (composed of 16 bit integers) | | c | | pev(1)=float(ev(1))* | | * float(ev(2)**2) / 1.0e6 | | pev(2)=ev(3)+ev(4) | | c | | c the function value returned by USER is ignored by sort program| | c | | return | | entry user1 (et,np,ev,lnzvec) | | c | | c called once for each event when the condition "USER 1" is | | c evaluated | | c it will not be called if it was preceded by a KILL condition | | c which caused the event to be "killed" | | c | | c it can compute pseudo-parameters based on linearization | | c values if the user has made sure that the linearization has | | c been evaluated - see main text | | c | | c argument 1 is the real event vector (16 bit integers) | | c argument 2 is the linearization vector (32 bit integers) | | c | | pev(3)=lnzVec(1)*1000.0/ev(4) | | c | | c the USER1 USER2 ... USER50 routines (unlike the plain USER) | | c can pass back a logical value to sort program which will be | | c the value of the corresponding condition | | c | | user1=ev(5) .gt. ev(6) | | return | | end | +------------------------------------------------------------------+ Daphne User-Functions Page 26 Arithmetic Traps on the Event Processor 18 July 1991 2 Arithmetic Traps on the Event Processor When an arithmetic trap occurs in a user-function executing on an Event Processor, the sort program drops any effort at further analysis of the event, and transfers control to the end of the sorting routine, which copies an event to the tape buffer for later recording. If event mode recording is enabled, the event will be copied to the output tape buffer, unless a NOTAPE condition was found to be true prior to the arithmetic trap. The number of such arithmetic traps is counted by the sort program and the first, 100 th, and 10,000 th cause a message to be sent to the user's command terminal along with specific information to aid in locating the problem. The variable which counts the number of traps is reset at the beginning of each run. The debug information is put in the file "ttt.TRAP" (where "ttt" represents the Daphne User ID) in the default diretory. An example of the trap report from an Event Processor is on the next page. Daphne User-Functions Page 27 Arithmetic Traps on the Event Processor 18 July 1991 +--------------------------------------------------------------------+ a | Arithmetic Trap in User Function Integer division by zero | | | b | Event Proc: 2 Date: 17-SEP-87 15:15:58 | | Experiment: Run number: | | Physicist: Target: | | Beam: Energy: | | Comments: | | | | Count of traps: 1 | | Event Type: 3 | c | User "Trap ID": 0 (dec) | | 0000 (hex) | | | | | | Absolute Address PC Relative Address | | (hex) (dec) (hex) (dec) | | ---------------- ------------------- | | | | PC at time of trap: A059 41049 0000 0 | | Address of User Init routine A0C4 41156 FFFFFF95 -107 | d | Address of USER routine: A030 41008 0029 41 | | ^ | | ############ # | | # See Text ###################### | | ############ | | | | ---------- E V E N T D A T A ---------- | | | | Word Hex Dec (Unsigned) Dec (Signed) | | ---- ------- -------------- ------------ | | | | 1 0003 3 | | 2 0000 0 | | 3 0080 128 | | | +--------------------------------------------------------------------+ Daphne User-Functions Page 28 Arithmetic Traps on the Event Processor 18 July 1991 I would like to point out some important information provided by this report. - Reason for Arithmetic Trap (line "a") The most common cause is division by zero. The second most common cause is floating point overflow due to assignment of a floating point number larger than 32,767 (or less than -32,768) to a Fortran INTEGER*2 variable, such as an element of the pseudo-event vector. The NS32000, unlike the VAX, does not have an arithmetic trap when an INTEGER*4 variable with a value too large to fit is assigned to an INTEGER*2 variable. The NS32000 simply ignores the most significant bits. - Event Processor Number (line "b") This assists in locating a processor with a malfunctioning floating-point unit. - Trap ID (line "c") The "Trap ID" is a substitute for the subroutine traceback-with-line-numbers provided by many Fortran systems. The GNX development system used by Daphne does have a source code symbolic debugger, but most users appear to prefer debugging programs with PRINT statements. Unfortunately, there are three drawbacks to debugging your Event Processor user-function with PRINT statements: - Memory Limitations The code necessary to support I/O requires too much memory when combined with the very large buffers used in the Daphne acquisition program. This limitation should disappear when the Event Processor memory boards are upgraded from 128 kBytes to 512 Kbytes. - Acquisition Slowed Down The PRINT statements are so slow that the acquisition rate would be practically zero. - Flood of Output Some problems occur only rarely, and the PRINT statement might be executed thousands of times before the problem appeared. Daphne User-Functions Page 29 Arithmetic Traps on the Event Processor 18 July 1991 When a user is having difficulty locating the problem in the program, a few extra statements can be added to the code: call trapID (10) call trapID (11) call trapID (12) etc. The trapID routine (Section 1.13) records the value passed to it. It is a quick subroutine call: the body of the subroutine is a single "move word" instruction. +-----------------------------------------------------------------+ | Recommended for Event Processors Only | +-----------------------------------------------------------------+ | subroutine trapID (idWord) | | | | idWord - input/integer*2 | | word to be reported to host computer when | | there is an arithmetic trap | +-----------------------------------------------------------------+ Should an arithmetic trap occur, the trap recovery routine passes the last trap ID number (in the example above: 10, 11, or 12) to the host to be included in the error message, thus identifying the block of code containing the problem. This is not recommended for the VAX because the VAX/VMS symbolic debugger provides a much better tool for isolating problems. Daphne User-Functions Page 30 Arithmetic Traps on the Event Processor 18 July 1991 - PC at Time of Trap (line "d") The PC (program counter) is the address of the instruction causing the trap. If your user-function is written as a single subroutine starting with entry point USER or USERIN, it can be relatively easy to locate the instruction causing the problem. Assume, for the purpose of this example, that your user function begins with USER (rather than USERIN). Note the value (in hexadecimal) of the item "PC address relative to the address of USER" (In the example above the value is "0029" at line "d"). If you compile your Fortran program with the "DAPEP:EPBUILD" command procedure, the Fortran compiler will create a file containing the machine code generated by the compiler, interspersed with "comment lines" containing the Fortran code responsible for the machine code which follows it. If you use the Fortran optimizer, the relationship between the Fortran comment lines and the generated machine code may break down because an optimized program will not necesarily perform operations in the same order as a non-optimized program. On the second page following, the lines preceded by "#---" are the Fortran statements inserted as comment lines in the assembler listing. Assume your user function is in the file "ICE.FOR". The assembler source code will have been placed in the file "ICE.ASM" by the Fortran compiler. Assemble the assembler source code with the following command to create a list file "ICE.LIS": $ NASM/LIST ICE Compare the "PC address relative to the the user-function" with the location counter (the number preceded by the letter "T") listed in the second column down the left side of the listing file. When you find a match, look above it to find the Fortran code associated with the machine code. In the following example, the program will produce a division by zero in computing pev(1) when both ev(3) and ev(4) are zero. The PC relative address would be equal to 0018 (the offset of the "quotient" instruction relative to the beginning of the program). This is line 26 of the assembler list file appearing on the second page following. Daphne User-Functions Page 31 Original Fortran Program 18 July 1991 +--------------------------------------------------------------+ | subroutine user (et,np,ev) | | implicit undefined (a-z) | | save | |c | | integer*2 et | | integer*2 np | | integer*2 ev (*) | |c | | integer*2 pev (20) | |c | -> | pev(1)=(ev(3)-ev(4))/(ev(3)+ev(4)) | | return | |ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | | entry userin | | call defPEV (pev) | | return | |c | | end | +--------------------------------------------------------------+ Daphne User-Functions Page 32 Fortran Annotated Assembly Language Output 18 July 1991 +------------------------------------------------------------------+ | | | | | 9 #--- implicit undefined (a-z) | |10 #--- save | |11 #--- integer*2 et | |12 #--- integer*2 np | |13 #--- integer*2 ev (*) | |14 #--- integer*2 pev (20) | ->|15 #--- pev(1)=(ev(3)-ev(4))/(ev(3)+ev(4)) | |16 .align 4 | |17 _user_ : | |18 T00000000 7ca504 adjspb $(4) | |19 T00000003 eac00000 br .L3 | | 49 | |20 .align 4 | |21 .L4: | |23 T0000000c 21881006 subw 6(16(sp)),r0 | |24 T00000010 55881004 movw 4(16(sp)),r1 | |25 T00000014 41881006 addw 6(16(sp)),r1 | ->|26 T00000018 ce3108 quow r1,r0 | |27 T0000001b 55050000 movw r0,.L6_pev | | 005c | |28 #--- return | |29 T00000021 eac00000 br .L2 | | 23 | |30 #--- entry userin | |31 #### br .L7 | |32 T00000026 a2a2 .align 4 | |33 _userin_ : | |34 T00000028 7ca504 adjspb $(4) | |35 T0000002b eac00000 br .L6 | | 29 | |36 .align 4 | |37 .L7: | |39 T00000030 67ae0000 addr .L6_pev,0(sp) | | 005c00 | | 0000 | |42 T0000003d eac00000 br .L2 | | 07 | |44 T00000042 a2a2 .align 4 | |45 .L2: | |47 T00000044 7ca5fc adjspb $(-4) | |48 T00000047 1200 ret $(0) | | | | | +------------------------------------------------------------------+ The arithmetic trap was caused by the "quow" instruction (quotient for word length operands) appearing at line 26. Please note that the Fortran code appearing at line 15 caused code to be generated for lines 16 through 27. Actually, lines 16 through 20 are subroutine "prologue", and have to do with subroutine initialization, rather than the computation of pev(1). They are automatically generated at the first appearance of an executable Fortran statement.