1 / 17

Writing a Primitive or Task

SCT/Pixel DAQ Workshop. Writing a Primitive or Task. Five easy steps to make a primitive. The 6 th step…. Seven not-quite-as-easy steps to make a task. About that 8 th step…. Douglas Ferguson UW-Madison & LBL. Writing a Primitive, Step 1.

tse
Download Presentation

Writing a Primitive or Task

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

E N D

Presentation Transcript


  1. SCT/Pixel DAQ Workshop Writing a Primitive or Task • Five easy steps to make a primitive. • The 6th step… • Seven not-quite-as-easy steps to make a task. • About that 8th step… Douglas Ferguson UW-Madison & LBL

  2. Writing a Primitive, Step 1 In primParams.h, define the constant mnemonic for the primitive ID, the revision number and structure tags for the input and output (reply) data. #define CHECK_BIT (1 + (<previously last primitive>)) #define R_CHECK_BIT 100 struct CHECK_BIT_IN { UINT32 registerId; UINT32 bitNumber; }; struct CHECK_BIT_OUT { UINT32 bitSet; }; #define LAST_COMMON_PRIMITIVE (CHECK_BIT) NOTES: • To avoid forcing re-writes of pre-existing primitive lists, this should be done • at the end of the section: common, slave or master primitives. • There is no need to define structures for primitives with un-structured data, • like Echo.

  3. Writing a Primitive, Step 2 In primParams.h, near the bottom, put a prototype declaration for Your primitive in the appropriate section: INT32 checkBit(struct PRIM_DATA *); Writing a Primitive, Step 3 In primParams.h, increment the PRIM_LIST_REVISION compiler Constant, if you are brave. NOTES: • While it’s a good idea to do step 3 in theory, in practice it causes endless • compatibility problems. For individual primitives, there are already revision • numbers. Changing that when you change the # of input parameters for a • primitive is a good idea. My suggestion: We leave the compiler constant • fixed, unless the primitive list structure is radically altered.

  4. Writing a Primitive, Step 4 Update primIDList & the primParameters array inside initializePrimParams in primFuncts.c; add in the new primitive in the appropriate spot (bottom of common, slave-only, or master-only functions): primIDList[++primLoop]= CHECK_BIT; primParameters[primLoop].primFunction= checkBit; primParameters[primLoop].primRevision= R_CHECK_BIT; NOTES: • The primParameters array is initially set to point to a special routine called • noPrimitive. If this step is forgotten, it will still point there and an error • will be generated if the primitive is called. • It’s not strictly necessary to keep the primParameters & primIDList arrays • in the same order as they appear in primParams.h (where they MUST go at the • end of their section), but it helps readability.

  5. Writing a Primitive, Step 5 In primFuncts.c, slavePrimFuncts.c or masterPrimFuncts.c, write the routine... INT32 checkBit(struct PRIM_DATA *primData) { INT32 returnCode = SUCCESS; INT32 error; UINT32 registerId; UINT32 bitNumber; UINT32 bitSet; struct CHECK_BIT_IN *checkBitIn = (struct CHECK_BIT_IN *)primData->priBodyPtr; struct CHECK_BIT_OUT *checkBitOut = (struct CHECK_BIT_OUT *)primData->repBodyPtr; registerId = checkBitIn->registerId; bitNumber = checkBitIn->bitNumber; primData->repBodyLength = SIZEOF(struct CHECK_BIT_OUT); NOTES: • If your primitive will be polling or will otherwise return REPEAT_PRIMITIVE • under some circumstances, static variables are needed. • ALWAYS set the size of the reply data in the function, as early as is • feasible. Not doing so will cause un-ending amounts of grief.

  6. Writing a Primitive, Step 5 (cont) error = readRegister( registerId, 1, bitNumber, &bitSet); if (error < 0) { addError(&returnCode,error, "checkBit","readRegister", __FILE__, __LINE__); if (FATAL(returnCode)) { return returnCode; } } checkBitOut->bitSet = bitSet; return returnCode; } NOTES: • addError adds a function’s return INT32 error code (negative if an error) to • the returnCode, and concatenates the identifier strings to the error message • which is already in the error buffer. If the error originates inside the primitive • itself, use newError instead. • If the function returns large amounts of data, consider returning a pointer & • data length instead, like sendData. Failing that, check the reply data’s length • against the length of the reply buffer.

  7. Writing a Primitive, Step 5 (cont) endPtr= primData->repBodyPtr +wSize; if (endPtr > primData->repBuffEnd) { newError(&returnCode, MSG_EXCEEDS_LIST_BOUND, ERROR_0, "sendData", "Reply data would exceed reply buffer boundary! ...sending pointer", __FILE__, __LINE__); return returnCode; } NOTES: • Don’t forget to update the revision number if you change the number or • meaning of the input or output parameters! Another source of grief.

  8. Writing a Primitive, Step 6 Compile and debug the routine! Arrgh!!

  9. Writing a Task, Step 1 In primParams.h, define the task’s revision number and structure tags for the input and output (reply) data, if any. The new task should be defined in the section in primParams.h where all the other tasks are defined (in the startTask section). Add the new structure to startTask’s structure union. Tasks must have an input structure of some sort defined. The union cannot accept unstructured data, like echo. #define R_TRAP_TASK 100 struct TRAP_TASK_IN { UINT32 nEvents, reloadInterval, trapType, eventType, *trapBufferBase, trapBufferLength; }; union TASK_STRUCTURES_IN { struct HISTOGRAM_CTRL_TASK_IN histoCtrlTaskIn; struct MIRROR_TASK_IN mirrorMemoryTaskIn; struct TRAP_REQ_TASK_IN trapRequestTaskIn; struct HISTOGRAM_TASK_IN histogramTaskIn; struct TRAP_TASK_IN trapTaskIn; struct OCCUPANCY_TASK_IN occupancyTaskIn; struct ERROR_TASK_IN errorTaskIn; struct RESYNCH_TASK_IN resynchTaskIn; };

  10. Writing a Task, Step 2 In primParams.h, if the task has output data (variables, not processed event data), add the new output structure to taskOperation’s structure union. If tasks output data, it must have a defined structure. A generalized output structure is available for simple output. The trapping task uses the generalized output structure: struct GEN_TASK_OUT { UINT32 *dataPtr, dataLength; }; union TASK_STRUCTURES_OUT { struct HISTOGRAM_CTRL_TASK_OUT histoCtrlTaskOut; struct HISTOGRAM_TASK_OUT histogramTaskOut; struct ERROR_TASK_OUT errorTaskOut; struct GEN_TASK_OUT genTaskOut; };

  11. Writing a Task, Step 3 In primParams.h, add the task to the appropriate task ID list. There are no common tasks yet, only master & slave DSP tasks; if a common task is needed a new section will have to be created & several routines need updating. The task should generally go at the bottom of the list, especially if it is an event-based slave DSP task. /* slave DSP tasks: */ #define SLAVE_TASK_BASE (0x20) #define HISTOGRAM_TASK (SLAVE_TASK_BASE) #define TRAP_TASK (1+(HISTOGRAM_TASK)) #define OCCUPANCY_TASK (1+(TRAP_TASK)) #define ERROR_TASK (1+(OCCUPANCY_TASK)) #define RESYNCH_TASK (1+(ERROR_TASK)) #define LAST_SLAVE_TASK (RESYNCH_TASK) #define NUM_SLAVE_TASKS ((LAST_SLAVE_TASK)-(SLAVE_TASK_BASE)+1) • DO NOT forget to update the maximum number of tasks (master or slave), if • that has changed. Otherwise you will get mysterious errors, with no idea why… /* MAX_NUM_TASKS is the maximum of the two task numbers for master & slave DSPs. Bad Things can happen if it is not kept up to date */ #define MAX_NUM_TASKS 5

  12. Writing a Task, Step 4 If the task is event based, in primParams.h update the trapping function field of eventTrapSetup. There are not many more fields available, so be conservative with these. You will also need to update eventHandler.h, and the event processing routines in eventHandler.c Processed data should be handled by sendData. #define TRAP_FXN_HISTOGRAM 1 #define TRAP_FXN_TRAP 2 #define TRAP_FXN_OCCUPANCY 4 #define TRAP_FXN_ERRORCNT 8 #define TRAP_FXN_RESYNCH 16 Frame set registers: • 32 per frame set, used by the event manager to jump from one frame to the • next when processing multi-frame events, and determine which tasks to give them to. • defined as 0xSSSD DDDD PPPP PETO FFFF FFFF IIII IIII Useful for monitoring; can be decreased or removed if needed. Frame set delta Delta to Next within Frame set Pending tasks Evt frame # Internal ID Overflow bit Trailer bit Error bit

  13. Writing a Task, Step 5 In the appropriate c file, write the function prototype. The following routines will also need updating: insertTask, taskOp, And getTaskData. INT32 trapTask(union TASK_STRUCTURES_IN *, union TASK_STRUCTURES_OUT *, UINT32); Writing a Task, Step 6 Define the new tasks’ states. Your task will probably be executing for a while, and it will be helpful if the DAQ can know what mischief it’s up to.. (see my DSP software talk for a good example of task states. /* TASK_INIT == 1 */ #define HCTRL_NEWBIN 2 #define HCTRL_WAITEXP 3 #define HCTRL_PULSING 4 #define HCTRL_WAITING 5 #define HCTRL_PREP 6 /* TASK_DONE == 0xD */

  14. Writing a Task, Step 7 In the appropriate c file, write the task.. #define MIRROR_RUNNING 0x2 #define MIRROR_WAITING 0x3 INT32 mirrorTask(union TASK_STRUCTURES_IN *tsi, union TASK_STRUCTURES_OUT *tso, UINT32 opFlags) { INT32 returnCode= REPEAT_TASK, slaveStat, errorCode; static UINT8 state= TASK_INIT, slvBits, nMirrorSlaves, first, warn[4], nibble, nMirrors= 0; static UINT32 hpic, *mirrorBase=(UINT32 *)(MIRROR_DEFAULT_BASE), mirrorTotLen= 0; static UINT32 mirrorFreq[MAX_MIRRORS], lastUpdate[MAX_MIRRORS], *mirrorSlvBase[MAX_MIRRORS], mirrorLen[MAX_MIRRORS]; UINT32 word, *address, deltaTime, readTime, time; UINT8 i, slv, slv_idx, accum, stateChange; char startStr[40];

  15. /********************* data retrieval & routine reset ****************/ if (opFlags > 0x10) { /* Place the interesting internal variables into the output structure */ tso->genTaskOut.dataPtr= NULL; tso->genTaskOut.dataLength= 0; if (opFlags & TASK_HALT_MASK) { newInformation(__FILE__, __LINE__, "Memory Mirror task halting.\n"); state= TASK_INIT; return TASK_HALTED; } else if (opFlags & TASK_INIT_MASK) { newInformation(__FILE__, __LINE__, "Memory Mirror task re-initing.\n"); /* reset nMirrors, and the default base & length, which are not reset on task stopping to allow accumulation. */ nMirrors= mirrorTotLen= 0; mirrorBase= (UINT32 *) (MIRROR_DEFAULT_BASE); state= TASK_INIT; return TASK_INITIALIZED; }

  16. else if (opFlags & TASK_QUERY_MASK) { newInformation(__FILE__, __LINE__, "Memory Mirror task query data output.\n"); return TASK_QUERIED; } } Initialization state must handle initialization of all routine static variables: /*************************** routine states:INIT ******************/ else if (state == TASK_INIT) { /* set up any static variables beyond state */ returnCode= REPEAT_TASK; nibble= MIRROR_TASK -MASTER_TASK_BASE; first= TRUE; hpic= (1<<HHPI_HPIC_HWOB_SHIFT) | (1 <<(HHPI_HPIC_HWOB_SHIFT +HWORD_W)); . . .

  17. else if ((state == MIRROR_RUNNING)||(state == MIRROR_WAITING)) { if (first) { first= FALSE; taskMgrCtrl.runningTaskReg &= ~(0xf<<(nibble*4)); taskMgrCtrl.runningTaskReg |= (state<<(nibble*4)); } . . . Above lines tell DAQ what state task is in } return returnCode; }  If all went well, returnCode= REPEAT_TASK

More Related