| LAT Internal Communications System: Software Interface Conceptual Design | ||
|---|---|---|
| Prev | Chapter 2. Commands and Results | Next |
The ACRI offers a powerful way for users to queue command lists to the LCB for processing. While the LCB is busy processing the commands list and recieving result items the user can continue to do other work. The LICS notifies the user after the final command item and result item are processed. After notification the user would proceed to inspect the result list.
The LICS provides a data structure for organizing the command list and the result list called the LAT I/O Transaction (LIOX). Users communicate with the LICS via an opaque handle to a LIOX structure. A diagram showing the relationship between the command list, result list and LIOX structure is shown below in Figure 2-1.
In addition to organizing memory the LIOX also maintains an internal command pointer used when filling the command list. The command pointer behaves much like a file pointer in a file system – it always points to the memory location where the next command will be inserted. As commands are added to the command list the command pointer is updated to point to the next available command slot.
Before creating and sending command lists via the LICS a LIOX handle must first be properly initialized. This entails the following activities:
Allocate memory for the LIOX structure, the command list and the result list.
Implement a user-defined result processing function called by the LICS when signaled by the LCB that a result list is ready.
Initialize the LIOX by passing in pointers for the command list memory, for the result list memory and for the result processing function.
Memory allocation is left entirely to the user, i.e. the LICS never allocates memory of its own. The LICS uses memory previously allocated by the user.
The LCB places constraints on the memory allocated for command lists and result lists. In particular the memory for the lists must be visable to the PCI bus and have the size and alignment attributes shown in Table 2-1.
| Memory Space | LCB DMA | Size | Alignment |
|---|---|---|---|
| Command List | Read | Up to 4092 Bytes | 512 Byte |
| Result List | Write | Up to 4084 Bytes | 8 Byte |
Table 2-1. LIOX List Memory Attributes
It may be worthwhile for the LAT system software to offer memory allocators for different alignment requirements.
The user defined result processing function is called by the LICS after a command list is sent and the corresponding result list is ready for processing. From the user's point of view this function is called asynchronously, notifying the user that a result list is ready for processing.
The LIOX maintains a pointer to the result processing function.
For more information on result processing and the result processing function see Section 2.1.4.
The following pseudo-code illustrates the initialization of a LIOX Handle.
/* define result processing function */
static int myProcessResultFunc(...);
/* allocate memory for command list, result list and LIOX struct */
void *pCmdList; /* command list */
void *pRstList; /* result list */
void *pLIOX;
LIOXHandle lh;
/* allocate memory for the command list -- suitably aligned */
pCmdList = mallocCommandList();
/* allocate memory for the result list -- suitably aligned */
pRstList = mallocResultList();
/* allocate memory for the LIOX structure */
pLIOX = mallocLIOX( liox_SizeOfIOX());
/* create opaque handle to LIOX
lh = liox_Init( pLIOX, pCmdList, pRstList, myProcessResultFunc);
/* LIOXHandle lh is now initialized and ready for use */
|
![]() | The liox_Init() function may also take the sizes of the command list and result list as arguments. This would allow the LICS to perform bounds checking as commands are added to the command list. |
After the LIOX handle is initialized the internal command pointer points to the first available command slot in the command list. A mechanism exists for reseting the command pointer so that a LIOX handle can be re-filled with a new list of commands.
Commands are added to the command list by passing the associated LIOX handle as an argument to the family of "LAT Command Functions". The "LAT Command Functions" provide a basic interface for read/write and dataless commands to the various addressable objects within the LAT.
As of this writing 11 types of addressable objects exist within the LAT – this number is expected to grow to about 20 types of objects. Each object contains registers which are also addressable. The following object types exist today:
TEM Common Controller
TEM Trigger Interface Controller (GTIC)
Tracker Cable Controller (GTCC)
Tracker Readout Controller (GTRC)
Tracker Front End Controller (GTFE)
Calorimeter Cable Controller (GCCC)
Calorimeter Readout Controller (GCRC)
Calorimeter Front End Controller (GCFE)
AEM Common Controller
ACD Readout Controller (GARC)
ACD Front End Controller (GAFE)
For each object type there exists three "Command Functions" – register load, register read and dataless command. That is a total of 33 functions for 11 object types. A rough estimate projects 50-60 functions ( about 20 object types ) in the final system.
Example 2-1. Reading Calorimeter DAC
As an example consider the hypothetical case of reading a DAC on a calorimeter front end chip (GCFE). The reading of a DAC register corresponds to a register read "Command Function". To uniquely address a DAC register on a specific GCFE requires a TEM address, a GCCC address, a GCRC address, a GCFE address and the DAC register number. The GCFE read "Command Function" requires all these parameters as arguments. Below is the function prototype for liox_qGCFEload.
int liox_qGCFEread(LIOXHandle lh, short temId, short gcccId, short gcrcId, short gcfeId, short regId);
Calling liox_qGCFEread with appropriate arguments would add a GCFE read command to the command list of the LIOX handle. The LIOX handle would update its internal command pointer to point at the next available command slot in the list. An example is shown below:
unsigned int errVal; LIOXHandle lh; /* previously initialized */ /* queue the read command to the command list */ errVal = liox_qGCFEread( lh, temId, gcccId, gcrcId, gcfeId, DACregId); |
This process is repeated, adding commands to the LIOX handle until the entire command sequence is loaded or the LIOX handle runs out of memory. Next the command list is queued to the LCB for execution.
Once the command list of a LIOX handle is loaded the list is ready for execution. Queueing the command list to LCB is trivial from the user's perspective. A simple execute function, like the one below, is all that is needed.
Behind the scenes, however, the LICS performs various bookkeeping operations when queueing the command list. Refer to [1] for more details on queueing command lists (referred to as export lists).
The address of the command list and the length of the command list are queued to the LCB by the LICS – together the length and address are called the export descriptor. The length of the list is calculated from the internal command pointer.
After queueing the export descriptor the LCB takes over. The LCB DMAs the command list from the SBC's memory to the LCB's memory and sends the commands out on the command/response fabric of the LAT one by one.
As the results come into the LCB it stores the result items locally. After the final result comes in the LCB DMAs all the result items to the result list associated with the command list that initiated the commanding sequence. It next puts a result descriptor in the result FIFO for the SBC and fires an interrupt.
The LICS traps the interrupt and reads the result descriptor from the results FIFO. The LICS uses the result descriptor to locate the appropriate LIOX handle. From the LIOX handle the LICS calls the user supplied result processing call-back function.
[ Note to self: what might the return code from the user supplied call-back be used for ? ]
Once a result list is ready the user needs a way to process the result list and decode the result items within the list.
The LCB provides two types of result items, both of which are fixed in length. The first type of result item is a simple "transmission verification" result used for both load commands and dataless commands, which by their nature have no response data. This result type contains a transmission timestamp and error information.
The second type of result item is in response to a read command. In addition to the "transmission verification" data of the simple result type, the response type result item also has a payload containing protocol header information and the value read from the register.
The next sections describe methods provided by the LICS for navigating the results list and for decoding result items.
The LICS provides a basic mechanism for walking or iterating over the result list, item by item. The LIOX handle maintains a result pointer (see Figure 2-1) that points to the next available result item.
The LICS provides the following functions for result list navigation:
The liox_nextResultItem() function returns a pointer to the next ResultItem and updates the result pointer for the LIOX. When no more result items exist the functions returns NULL. The liox_rewindResults() resets the results pointer to the beginning of the result list.
A user can iterate over a result list as follows:
ResultItem *pItem = NULL;
LIOXHandle lh;
while ( (pItem = liox_nextResultItem( lh))) {
processItem( pItem);
}
|
In the above example the while loop terminates when liox_nextResultItem returns NULL.
As alluded to earlier two types of result items exist. The two types have some data fields in common while the "response" result type has more information and requires special decoding.
This section discusses functions used to access the common data fields for both result types.
Both result types have a timestamp field (24 bits), an error field (16 bits) and a result type bit. The following functions retrieve these fields from a ResultItem pointer.
unsigned intliox_riGetTimestamp(ResultItem *pItem);
unsigned intliox_riGetError(ResultItem *pItem);
intliox_riHasPayload(ResultItem *pItem);
The liox_riHasPayload() function test the result type bit and returns non-zero if that bit is set. This indicates that the result item is a response to a read command and has a payload.
Result items that contain payloads in response to read commands require extra decoding. All response result items contain two fields: a LATp Header and a data payload.
The LATp header is a 16 bit field, while the data payload is a 112 bit field ( 7 16-bit integers).
The following functions are available for decoding response result items:
unsigned shortliox_riGetHeader(ResultItem *pItem);
unsigned short*liox_riGetPayload(ResultItem *pItem);
In addition to the basic liox_riGetPayload() function the following helper functions might also be added for specific payload types.
unsigned shortliox_riGetPayload16(ResultItem *pItem);
unsigned intliox_riGetPayload32(ResultItem *pItem);
unsigned long longliox_riGetPayloadTKR64(ResultItem *pItem);
The liox_riGetPayload16() and liox_riGetPayload32() return the first 16 bits and 32 bits of the payload respectively. Most register reads fall into these two cases.
The liox_riGetPayloadTKR64() function is a special decoder for tracker register reads (GTRC and GTFE registers are 64 bits wide). The raw data from a tracker register read contains extra protocol bits every 16 bits – this function removes the protocol bits and returns only the 64 bit register value.
Other special decoders might be needed to facilitate decoding of special registers. The environmental registers of the GTIC is one example.
Example 2-2. Reading a Calorimeter DAC – Continued
Continuing the example started in Example 2-1 we will decode the 16 bit GCFE DAC value within our result list callback function. Assume the result list only contains a single result item, the result item that corresponds to the liox_qGCFEread() command.
int resultProcCallback( LIOXHandle lh) {
ResultItem *pItem = NULL;
unsigned int timeStamp;
unsigned int errorValue;
unsigned short int registerValue;
/* fetch the first result item from the LIOX handle */
pItem = liox_nextResultItem( lh);
if ( pItem != NULL) {
/* get the time stamp value */
timeStamp = liox_riGetTimestamp( pItem);
/* get the error value */
error = liox_riGetError( pItem);
/* test if this item has a result payload */
if ( liox_riHasPayload( pItem)) {
/* by design this item is a 16 bit GCFE register */
registerValue = liox_riGetPayload16( pItem);
/* do something useful with register value */
}
}
return OK;
}
|