/********************************************************************************** * simEvts_sct.c: Create inmems which mimic SCT modules. * * There are 2 ROD input memories, corresponding to the two EFB engines. Each * "inmem" is 48 bits wide, enough to hold 4 formatters' input (12 links per * formatter). Each module's data arrives on formatter links which are specified * using the module mask LUT (two links per module). Formatter links are given in * the format 0xFL: the upper nibble is the formatter while the lower contains the * link. The formula to translate from formatter +link to hardware link is thus * hw link= 12*(fmtLink>>4) +(fmtLink & 0xf). * * The module data format is explained in the ADCD3T ASIC Chip Specification * document. Briefly, events have a header, L1-ID and BC-ID fields with * synchronization bits after the latter. Then any contributing FE outputs its * hits, or a null-hit packet if none. Adjacent channels cluster. * * An example event would be: s= sync, L leader, F= fe/chip, C= channel, * D= BC crossing data (prev. current & next) * * header L1-ID BC-ID s L hit data s BCX info s BCX (adjacent hit * 11101 4 bits 8 bits 1 (01 Fx4, Cx7, 1 Dx3 ) (1 Dx3) * * +(any other hits) ...next chip etc. and finally: s +trailer 0s: * 1 15x0 * * * in this file: setInmemDefaults, checkInmemCtrl, buildInmem, addTimInfo * initData, addNoHitDataPackets, createLinkData, * findNextHit, addLinkData, correctLink, fifoErr. * * related files: * * simEvts.h & simEvts_sct.h: Defines the inmem data & control structures. * inmemFxn_sct.h: Inmem function prototypes. * * author: Douglas Ferguson. **********************************************************************************/ #include /* must be included 1st */ #include #include #include #include #include "resources.h" #include "serialStreams.h" #include "ABCDscans.h" #include "moduleMaskLUT_def.h" #include "drand48.h" #include "rodConfiguration.h" #include "simEvts.h" extern char genStr[]; extern TIMER_Handle timer1; /* general purpose timer, started in main() */ extern far ModuleMaskLUT moduleMaskLUT[]; #pragma CODE_SECTION(setInmemDefaults, "xcode"); #pragma CODE_SECTION(checkInmemCtrl, "xcode"); #pragma CODE_SECTION(buildInmem, "xcode"); #pragma CODE_SECTION(addTimInfo, "xcode"); #pragma CODE_SECTION(initData, "xcode"); #pragma CODE_SECTION(addNoHitDataPackets, "xcode"); #pragma CODE_SECTION(createLinkData, "xcode"); #pragma CODE_SECTION(findNextHit, "xcode"); #pragma CODE_SECTION(addLinkData, "xcode"); #pragma CODE_SECTION(correctLink, "xcode"); #pragma CODE_SECTION(fifoErr, "xcode"); //#pragma DATA_SECTION(simInmemCtrl, "xpdata"); //#pragma DATA_SECTION(simInmemData, "xpdata"); SimInmemCtrl simInmemCtrl, *sic; SimInmemData simInmemData, *sid; /********************************************************************************** * setInmemDefaults assigns default values to the inmem control structure. */ void setInmemDefaults(SimInmemCtrl *sic) { int i; sic->sid= &simInmemData; sic->nEvents= 1; sic->gaussian[0]= sic->gaussian[2]= 0; sic->gaussian[1]= 100; //no spread. for (i=0; ievtData[i].atlasId= 7*i; sic->evtData[i].timId= i & 3; sic->evtData[i].evtBitOffset= 40*i; sic->evtData[i].evtModules[0]= 0xffffffff; sic->evtData[i].evtModules[1]= 0xffff; } sic->depthLimit= 0x1000; sic->stageCnt= 4; sic->scanType= ST_QCAL; sic->links= 0x11; sic->bx= 2; //use the central (current) bunch crossing. sic->currentStage= 0; sic->makeTemplate= true; sic->createNullLinks= false; sic->cal= true; sic->incId= false; sic->makeStub= false; sic->rSeed= 0xffffffff; sic->wrapL1id= 16; sic->templateMod= 0xff; //indicates that the template not found yet. //sic-> = ; } /********************************************************************************** * checkInmemCtrl checks the inmem control structure's integrity. */ int checkInmemCtrl(SimInmemCtrl *sic) { int returnCode= 0, i, err= false; if (sic->nEvents > N_INMEM_EVTS) err= true; //Non-fatal error: set to the max. depth limit if exceeded: if (sic->depthLimit > INMEM_DEPTH) sic->depthLimit= INMEM_DEPTH; //The gaussian measures spread in the data in bunch crossings (bx): if ((sic->gaussian[0] +sic->gaussian[1] +sic->gaussian[2]) != 100) err= true; for (i=0; ievtData[i].evtBitOffset >= sic->depthLimit) err= true; /* Last event must begin before the end of the TIM FIFO, and have enough space left over to store the type information. The event data can continue past the 102 us TIM limit as the inmems are bigger. The limit for L1As is set at 96 us (0xf00). Consecutive events must be at least six clocks apart as well. */ for (i=0; inEvents; ++i) { if (sic->evtData[i].evtBitOffset > 0xf00) {err= true; break; } if (sic->evtData[i].evtBitOffset > sic->depthLimit) {err= true; break; } if (i>0) { if ( (sic->evtData[i].evtBitOffset) <= (sic->evtData[i-1].evtBitOffset +6)) {err= true; break; } } } /************************* SCT Specific Data *******************/ if (sic->cal) { sic->stageCnt= 4; if ((sic->scanType == ST_NMASK) || (sic->nmask)) err= true; //Calibration events must have stage= 0, 1, 2 or 3: if (sic->currentStage >= sic->stageCnt) err= true; } else if ((sic->nmask) || (sic->scanType == ST_NMASK)) { //Set both if either is set: sic->nmask= true; sic->scanType= ST_NMASK; if (sic->currentStage >= NCHAN) err= true; //current stage => nmask cnt. } //Event must come in on both links, or 6 or 12 chips over 1 link: if ( (sic->links != 0x11) && ((sic->links != 0x01) && (sic->links != 0x10)) && ((sic->links != 0x02) && (sic->links != 0x20)) ) err= true; if (err) returnCode= -1; return returnCode; } /********************************************************************************** * buildInmem: The inmem-building interface to the rest of the program. The inmems * are constructed one event at a time, checking the integrity of the * resulting inmem buffers as the work progresses. Within an event, modules are * added one at a time. */ int buildInmem(SimInmemCtrl *sic) { int returnCode= 0, status; uint8 mod, mod1, testIdx, evt, bcid, i; /* Seed the random # generator with either the system time (default) or a custom random seed: */ if (sic->rSeed == 0xffffffff) srand48(TIMER_getCount(timer1)); else srand48(sic->rSeed); sic->dataInitialized= false; initData(sic); //Clear the arrays at start. for (mod=0; modlastEvtEnd[mod]= 0; for (evt= sic->evt= 0; evtnEvents; ++sic->evt, ++evt) { /* For FIFO consistency, all BC-ID are referenced to the (arbitary) value of the 1st event's "BC-ID" (input). So, if another event "arrives" 10 clocks later, its BC-ID will be 10 greater than the previous event's BC-ID. reported BC-IDs are 8 bit fields and so wrap at 256. L1-IDs increment from the intial value & the reported L1-Id wraps at 16. This is only done if the incId flag is set, so that calibration-only inmems (which do not use the TIM fifo) will play out without errors. For greatest flexibility, the wrap value is settable (defaults to 16). */ if (sic->incId) { bcid= 0xff & ( sic->evtData[evt].evtBitOffset -sic->evtData[ 0].evtBitOffset +sic->bcidInit); sic->evtData[evt].bcid= bcid; sic->evtData[evt].l1id= (evt +sic->l1idInit); if (sic->wrapL1id) sic->evtData[evt].l1id &= (sic->wrapL1id -1); } else { sic->evtData[evt].bcid= sic->bcidInit; sic->evtData[evt].l1id= sic->l1idInit; } //Modify the TIM buffer to include this L1A & the event's ID information: addTimInfo(sic); //Fill in the event hit arrays on a module by module basis: for (mod=0; modnEvtBits[mod]= 0; sic->mod= mod; if (mod < 32) {mod1= mod; testIdx= 0; } else {mod1= mod -32; testIdx= 1; } if (!(sic->evtData[evt].evtModules[testIdx] & (1<<(mod1)))) { if (!sic->createNullLinks) continue; //Module not in event. sic->nullLink= true; } else sic->nullLink= false; /* Handle the controller buglet (1st event gets stuck in the formatters) with a makeStub flag. If this is set, treat event 0 especially to create a null event- this ought to overcome the inmem calibration playback (on L1A) buglet seen in the latest fpga code. The 1st null event will get stuck in the formatters, and subsequent events may push out the previously trapped event (sometimes it just accumulates). The MDSP will route the resulting null (or timeout error) events to zen country by assigning it a ROD id= 42 using the user mask set (4).*/ if ((evt == 0) && (sic->makeStub)) sic->nullLink= true; //Get the module's data links: for (i=0; i<2; ++i) { sic->fmtLink[i]= moduleMaskLUT[mod].fmtLink[i]; sic->hwLink[i]= 12*(sic->fmtLink[i]>>4) +(sic->fmtLink[i] & 0xf); } if (sic->hwLink[0] < 48) { sic->bidx= 0; } else { for (i=0; i<2; ++i) {sic->hwLink[i]-= 48; } sic->bidx= 1; } if (!sic->nullLink) { if ((sic->makeTemplate) && (sic->templateMod == 0xff)) {//set template: sic->templateMod= mod; sic->templateBuff= sic->ibuff[sic->bidx]; for (i=0; i<2; ++i) sic->templateLink[i]= sic->hwLink[i]; sic->nTemplateBits= 0; sic->isTemplate= true; } else {sic->isTemplate= false; } } //SCT chips add a no-hits data packet if nothing else: if (sic->nullLink) addNoHitDataPackets(sic); //Create the data if needed: if ((!sic->nullLink) && ((sic->isTemplate) || (!sic->makeTemplate))) { initData(sic); createLinkData(sic); } /* Event data begins arriving ~1.5 us after the L1A, or ~0x40 clocks after the L1A */ sic->evtStart= sic->evtData[evt].evtBitOffset +0x40; /* events can stack on a given link (if the last event was not yet finished playing through the formatters yet): */ if (sic->lastEvtEnd[mod] > sic->evtStart) sic->evtStart= sic->lastEvtEnd[mod]; sic->room= sic->depthLimit -sic->evtStart; if (sic->isTemplate) sic->templateStart= sic->evtStart; /* Check if there is enough room to add the event to this link; the routine will quit if there is not enough to add even the minimal event (as the partially created inmem for the other links is now useless), otherwise the routine will erase all hits for this module. */ status= correctLink(sic); if (status != 0) return status; addLinkData(sic); //Add the module's data to the inmem. } //Modules in event loop. } //Event loop. return returnCode; } /********************************************************************************** * addTimInfo: Adds the TIM info into its fifo. Note that the TIM fifo is not * available in simulated calibration mode (the MDSP sends the L1As) * and thus the L1-id must be set to the same value for each event which is * sent from a different set of data links (typically one per SDSP) and the * BC-id should be left at zero. The MDSP will signal to the controller FPGA * that the BC-id is static (default setting is 0). If the BC-id is set to a * non-zero value in this mode, the ROD's calibration BC-id register should be * set to match it. Alternatively, the BC-ids can be allowed to increment as * normal, and the EFB's link error supression registers must be then set to * ignore the resulting BC-id errors. */ void addTimInfo(SimInmemCtrl *sic) { uint8 i, timL1, bcid0, atlasId, timId; uint32 b, bL1; //L1-ID from TIM is offset by 1 from module's L1-ID: timL1= (sic->evtData[sic->evt].l1id -1); if (sic->wrapL1id) timL1 &= (sic->wrapL1id -1); bcid0= sic->evtData[sic->evt].bcid; atlasId= sic->evtData[sic->evt].atlasId; timId= sic->evtData[sic->evt].timId; /* The L1A is signaled as a single '1' bit in the L1A line from the (simulated) TIM. The other fields (L1 & BC IDs, Atlas/L1 & TIM event types) are signaled by a 'wake-up' bit, followed by the field information from LSB to MSB. The L1 & BC-ID information typically arrives ~2 us after the L1A, followed by the type information in another ~2 us (these can be stacked-- nyi). */ b= sic->evtData[sic->evt].evtBitOffset; sic->timBuff[b] |= 1<< L1A_BIT; // Add ID wake-up bit, then L1-ID (24 bits) & BC-ID (12 bits): b+= 0x50; bL1= b; sic->timBuff[b++] |= 1<< ID_BIT; for (i=0; i<24; ++i,++b) {sic->timBuff[b] |= ((timL1 & (1<<(i)))? 1:0)<timBuff[b] |= ((bcid0 & (1<<(i)))? 1:0)<timBuff[b++] |= 1<< TYPE_BIT; for (i=0; i<8; ++i,++b) {sic->timBuff[b] |= ((atlasId & (1<<(i)))? 1:0)<timBuff[b] |= ((timId & (1<<(i)))? 1:0)<sid->linkData[i]= sic->sid->hitSize[i]= 0; } if (sic->dataInitialized) return; //nothing else to do... for (fe=0; fesid->feData[fe]= 0; sic->sid->feLink[fe]= 0xff; sic->sid->feProc[fe]= false; for (chan=0; chansid->hitData[fe][chan]= 0; } } sic->dataInitialized= true; } /********************************************************************************** * addNoHitDataPackets: SCT chips issue a no-hit data packet if they are otherwise * empty; this routine signals to the rest of the inmem * building routines that they must include these packets. */ void addNoHitDataPackets(SimInmemCtrl *sic) { uint8 i, fe, link; for (i=0; i<2; ++i) sic->sid->hitSize[i]= 0; for (fe=0; fesid->feLink[fe]= 0xff; sic->sid->feProc[fe]= false; if ((fe == 6) || (fe == 7) || (fe == 14) || (fe == 15)) continue; if (sic->links == 0x11) link= (fe >= 8); else if ((sic->links == 0x01) || (sic->links == 0x02)) link= 0; else if ((sic->links == 0x10) || (sic->links == 0x20)) link= 1; if ((sic->links == 0x01) && (fe >= 8)) continue; else if ((sic->links == 0x10) && (fe < 8)) continue; /* If the links flag indicates that this link does not contribute, the routine signals this by setting feLink[fe] to 0xff (above). Otherwise, at minimum each chip contributes a no-hit packet; this is signalled by setting the feLink variable to the link number it contributes to, and incrementing the hitSize for the link. */ sic->sid->feLink[fe]= link; sic->sid->hitSize[link]+= 3; } //fe } /********************************************************************************** * createLinkData: Creates the hit pattern within a module. */ void createLinkData(SimInmemCtrl *sic) { uint8 fe, chan; int cs, cstart, cinc, cstop, bx, clusterLast, cluster, link; int useGaussian; float x; useGaussian= ((sic->gaussian[0] != 0) || (sic->gaussian[2] != 0)); bx= sic->bx; cs= sic->currentStage; if (sic->nmask) {cstart= cs; cstop= 128; cinc= 1; } else if (sic->cal) {cstart= cs; cstop= 128; cinc= 4; } else {cstart= cstop= 0; cinc= 1; } sic->cstart= cstart; sic->cstop= cstop; sic->cinc= cinc; /* Use the addNoHitPackets routine to get the fe/link association, and then later (see below) erase the extra 3 hits it generates if there is actual data: */ addNoHitDataPackets(sic); bx= 2; if ((sic->cal) || (sic->nmask)) { //Loop over hitData, filling it in using the selected hit pattern: for (fe=0; fesid->feLink[fe]; if (link == 0xff) continue; //Was set by addNHDP. clusterLast= sic->cstart; cluster= false; for (chan=sic->cstart; chancstop; chan+=sic->cinc) { if (useGaussian) { x= 100*drand48(); if (((int) x) < sic->gaussian[0]) bx= 4; else if (((int) x) >= 100 -sic->gaussian[2]) bx= 2; else bx= 1; } cluster= (chan == (clusterLast +1)); clusterLast= chan; /* Calculate the space required: Any non-sequential hit in a chip => 17 bits; clustered hits (no gap between the current and last channel) => 4 bits. Minimum is 3 bits/chip. */ if (!cluster) sic->sid->hitSize[link]+= 17; else sic->sid->hitSize[link]+= 4; sic->sid->hitData[fe][chan]= (link<<15) +bx; ++sic->sid->linkData[link]; ++sic->sid->feData[fe]; } //chan //Subtract the previously added NHDP if there actually was data: if (sic->sid->feData[fe]) sic->sid->hitSize[link]-= 3; } //fe } else { //"Data" event } //We have added data; if the routine is run again it will need re-initializing: sic->dataInitialized= false; } /********************************************************************************** * findNextHit: Returns the hit information for the next hit to the inmem building * routine. */ int findNextHit(SimInmemCtrl *sic, uint8 link, uint8 *xfe, uint8 *xchan, uint8 *xclus, uint8 *bx) { uint8 fe, chan; uint16 hitData; for (fe=0; fesid->feLink[fe] != link) continue; /* Chips with no hits contribute a no-hit data packet to the link they're associated with. Setting the feProc flag then assures that they will be skipped the next time the routine is called for that link (to find more data). Also, when the template is added to the inmem buffer, feData[] is decremented as hits are found and added. So null-links will always find feData[fe]= 0 for all the chips. The NHDP is signalled to addLinkData, the calling routine, by a pseudo-hit in a non-physical channel (128): */ if ((!sic->sid->feData[fe]) && (!sic->sid->feProc[fe])) { sic->sid->feProc[fe]= true; *xfe= fe; *xclus= false; *xchan= NCHAN; return true; } if (!sic->sid->feData[fe]) continue; for (chan=sic->cstart; chancstop; chan+=sic->cinc) { hitData= sic->sid->hitData[fe][chan]; if (!hitData & 0x7) continue; //no hit. if (((hitData & (1<<15))>>15) != link) continue; //not this link. *xclus= ((fe == *xfe) && (chan == (*xchan +1))); *xfe= fe; *xchan= chan; *bx= hitData & 0x7; sic->sid->hitData[fe][chan]= 0; --sic->sid->feData[fe]; --sic->sid->linkData[link]; sic->sid->feProc[fe]= true; return true; } //Channel loop. } //Chip loop. return false; } #define headerBits 0x3a #define headerLen 6 #define trailerLen 15 #define l1idLen 4 #define bcidLen 8 #define leaderLen 2 #define chipLen 4 #define chanLen 7 #define dataLen 3 /********************************************************************************** * addLinkData: Adds the current module's header, data, and trailer to the inmem. */ void addLinkData(SimInmemCtrl *sic) { //int nBits, lBits[2], rBits; //uint8 *templateByte, *byte, *endByte[2], *lastByte, bit, templateBit, int nBits, rBits, lBits[2]; uint8 *templateByte, *byte, *endByte[2], *lastByte, bit, templateBit, useTemplate, link, i, l1id, bcid, bitVal, fe, chan, clus, bx, lastfe; useTemplate= ((sic->makeTemplate) && (!sic->nullLink) && (!sic->isTemplate)); l1id= sic->evtData[sic->evt].l1id; bcid= sic->evtData[sic->evt].bcid; for (link=0; link<2; ++link) { byte= sic->ibuff[sic->bidx]; byte+= (6*sic->evtStart +(sic->hwLink[link]>>3)); // >>3 ==> /8 bit= sic->hwLink[link] & 7; // & 7 ==> %8 nBits= 0; rBits= sic->nTemplateBits; if (useTemplate) { templateByte= sic->templateBuff; templateByte+= (6*sic->templateStart +(sic->templateLink[link]>>3)); templateBit= sic->templateLink[link] & 7; } //Add header (MSB to LSB): for (i=headerLen; i>0; --i){ bitVal= (headerBits & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (l1id & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (bcid & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (1 & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (1 & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (fe & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (chan & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i){ bitVal= (bx & (1<<(i-1)))? 1:0; *byte|= bitVal<0; --i) {++nBits; byte+= 6;} } //not using template. else { //Copy (hit) data from the template link. while (rBits>0) { bitVal= (*templateByte & (1< earliest possible start of the next event, if any), by removing the hardware link offset to get the current byte for hardware links 0-7, and dividing by the width of the inmem FIFO (in bytes). Note that for asymmetric link data lengths, a real module would not be prepared in any case to send more data over either link until the last event had played out, so the links are treated as if both have the same amount of data. */ for (i=0; i<2; ++i) endByte[i]-= (sic->hwLink[i]>>3); lastByte= (endByte[1]>endByte[0])? endByte[1]:endByte[0]; sic->nEvtBits[sic->mod]= (lBits[1]>lBits[0])? lBits[1]:lBits[0]; sic->lastEvtEnd[sic->mod]= ((uint32) (lastByte -sic->ibuff[sic->bidx]))/6; if (sic->isTemplate) sic->nTemplateBits= sic->nEvtBits[sic->mod]; } /********************************************************************************** * correctLink: Checks that the inmem buffer's bounds are not about to be exceeded * by a call to addLinkData. */ int correctLink(SimInmemCtrl *sic) { int returnCode= 0; uint32 idx, min; //Minimum is (# evts) * (header +L1-ID (4) +BC-ID (8) +sync +trailer) min= (6 +4 +8 +1 +16); if (sic->room <= min) {fifoErr(sic, min, "minimum"); return returnCode= -1; } if (!sic->nullLink) { /* Get data length for modules which serve as template (this will be all modules if not using templates). */ if ((sic->isTemplate) || (!sic->makeTemplate)) { idx= (sic->sid->hitSize[0] > sic->sid->hitSize[1])? 0:1; min+= sic->sid->hitSize[idx]; } else if (sic->makeTemplate) {min= sic->nTemplateBits; } //Use the template. } else { //addNoHitDataPackets updates the hitSize arrays: idx= (sic->sid->hitSize[0] > sic->sid->hitSize[1])? 0:1; min+= sic->sid->hitSize[idx]; } //Warn & erase hits if not enough room; done if either link exceeds limit. if (sic->room <= min) { initData(sic); fifoErr(sic, min, "entire"); sprintf(genStr, "==> no hits for module %d!\n", sic->mod); newInformation(__FILE__, __LINE__, genStr); } else { returnCode= 0; } return returnCode; } /********************************************************************************** * fifoErr: Prints out an error message if there would have been a data overflow. */ void fifoErr(SimInmemCtrl *sic, uint32 min, char *mf) { sprintf(genStr, "%s%s%s%s%d%s%x%s%x%s%x%s%x%s", "There was not enough room in the FIFO for the ", mf, " event data\n", "(evt #", sic->evt, ", fmtLink[2]= 0x", sic->fmtLink[0], ", 0x", sic->fmtLink[1], " needed= 0x", min, " & only 0x", sic->room, " is available).\n"); newInformation(__FILE__, __LINE__, genStr); }