The analysis of the scan is carried out in the DSP code on the ROD as follows:
bestDelay = (points[worstSlice] + 9) % 24; // handle wrapping
So the RxDelay is set 9ns from the falling edge
best[1] = ((points[first]+points[last])*2)/3; //
And so the RxThreshold is set 2/3 of the way up in the region of 50% occupancy (assumed to be clk/2).
These values are used to change the settings of the BOC and are printed to a text buffer found in the SctApiCrateServer?*.out file.
Here is an example:

Useful additions
It would be a good idea to read the set points, either from the text buffer, or from the BOC. These could then be stored in a test result for later examination. Could also then put a dot on the analysis plot. Currently, they are present in the archived configuration, if the settings remain to the end of run.
Location of the DSP code
The DSP code for the analysis can be found in
./Dsp/Common/Code/masterPrimFuncts.cHere is some of it for handy reference:
INT32 bocScan(PrimData *primData) {
INT32 returnCode= SUCCESS, errorCode;
BocScanIn *primInput = (BocScanIn *) primData->priBodyPtr;
BocScanOut *primOutput = (BocScanOut *) primData->repBodyPtr;
// How many bits to capture
UINT32 width = primInput->width;
// Analysis on or off?
UINT32 algorithm = primInput->algorithm;
// Scan definition parameters
UINT32 scanParameter[2]; /* Scan parameter specifier */
UINT32 scanSize[2]; /* Number of points of each parameter */
/* Loop vars */
UINT32 link, x, y;
UINT32 fmt, pr, cnt, old_cnt;
UINT32 bin, nbins;
UINT32 best[2];
UINT32 outputLength;
UINT16 *buffer = (UINT16*)(primOutput+1);
UINT8 *points = (UINT8*)(primInput)+sizeof(BocScanIn);
// Check requested algorithm is valid
if(algorithm>1) {
newInformation(__FILE__, __LINE__, "Unknown algorithm for BOC_SCAN");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
// Check fifobuffer is big enough for input
// accessFifo returns [3*2*width] bytes
if(width > 0xFFFF){
newInformation(__FILE__, __LINE__, "Requested width exceeds counter width");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
if(width&0x0001){
newInformation(__FILE__,__LINE__,"Width decremented - even value required");
width--;
}
// Copy scan configuration information and calculate scanpoints
for(x=0; x<2; x++){
scanParameter[x] = primInput->scanParameter[x];
scanSize[x] = primInput->scanSize[x];
}
// Scan configuration information is read directly from the input primitive
// scanParameters[2]
// scanSize[2]
// scanPoints[]
nbins=scanSize[0]*scanSize[1];
// Allow RXDELAY only as outer loop parameter -
// this takes longer to set than the other parameters
if(scanParameter[1] == BOC_VAR_RXDELAY) {
newInformation(__FILE__, __LINE__, "BOC_RXDELAY allowed only as outer loop variable");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
if(nbins == 0){
newInformation(__FILE__, __LINE__, "No points requested!!");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
// Single slice, returning overall counts for 96 modules, so 96 (16 bit) words
outputLength = 96 * nbins;
// How many words are available in the primitive reply buffer?
cnt = primData->repBuffEnd - primData->repBodyPtr -2; //-2 => list trailer
// Check primitive buffer is big enough for output
// Output length is a word count, but MDSP_REP_BFR_SZ is in bytes
if(outputLength/2 > cnt){
sprintf(genStr, "repBodyPtr 0x%8x repBodyEnd 0x%8x freeWords 0x%8x outputLength 0x%8x",
primData->repBodyPtr, primData->repBuffEnd, cnt, outputLength);
newInformation(__FILE__, __LINE__, genStr);
newInformation(__FILE__, __LINE__, "Requested output length exceeds available space in primitive reply buffer");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}else{
sprintf(genStr, "nbins %d outputLength 0x%8x", nbins, outputLength);
newInformation(__FILE__, __LINE__, genStr);
}
setMem(buffer, outputLength, 0); // Clear buffer
sprintf(genStr,"First and last outer points: %d, %d",points[0],points[scanSize[0]-1]);
newInformation(__FILE__, __LINE__, "genStr");
sprintf(genStr,"First two inner points: %d, %d",points[scanSize[0]],points[scanSize[0]+points[scanSize[1]-1]]);
newInformation(__FILE__, __LINE__, "genStr");
// Main Loop
bin = 0;
for(x=0; x<scanSize[0]; x++){
errorCode = setBocVariableAll(scanParameter[0], points[x]);
if (errorCode < 0) {
addError(&returnCode, errorCode, "bocScan", "setBocVariable0", __FILE__, __LINE__);
if (FATAL(returnCode)) {
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
}
for(y=scanSize[0];y<(scanSize[0]+scanSize[1]);y++){
errorCode = setBocVariableAll(scanParameter[1], points[y]);
if (errorCode < 0) {
addError(&returnCode, errorCode, "bocScan", "setBocVariable1", __FILE__, __LINE__);
if (FATAL(returnCode)) {
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}
}
// Reset hardware counters
for(fmt=0; fmt<FORMATTERS_PER_ROD; fmt++){
writeRegisterDirect(FMT_COUNT_CTRL(fmt),32,0,0);
}
// Set hardware counters
for(fmt=0; fmt<FORMATTERS_PER_ROD; fmt++){
writeRegisterDirect(FMT_GLOBAL_CNT(fmt),16,0,width); // works
}
// Start Counting
for(fmt=0; fmt<FORMATTERS_PER_ROD; fmt++){
writeRegisterDirect(FMT_COUNT_CTRL(fmt),32,0,1);
}
// Wait for counting to stop
for(fmt=0; fmt<FORMATTERS_PER_ROD; fmt++){
old_cnt = 0xffffffff;
cnt = 0x0000ffff;
while(cnt>0){
readRegister(FMT_GLOBAL_CNT(fmt),16,16,&cnt);
if(cnt==old_cnt){ // STALLED!
newInformation(__FILE__, __LINE__, "STALLED");
// Set output count to 0
primOutput->count = 0;
primData->repBodyLength = 1;
return returnCode;
}else{
old_cnt = cnt;
}
}
}
// Read Counters
link = 0;
for(fmt=0; fmt<FORMATTERS_PER_ROD; fmt++){
for(pr=0;pr<(LINKS_PER_FORMATTER/2); pr++){
readRegister(FMT_PAIR_CNT(fmt,pr),16, 0,&cnt);
buffer[( link *nbins)+bin] = (UINT16) cnt;
readRegister(FMT_PAIR_CNT(fmt,pr),16, 16,&cnt);
buffer[((link+1)*nbins)+bin] = (UINT16) cnt;
link+=2;
}
}
bin++;
} // end inner loop
} // end outer loop
// sprintf(genStr,"BOCScan of %d bins completed - honest!",bin);
// newInformation(__FILE__,__LINE__,genStr);
if(algorithm>0){
UINT32 bestSlice=0xDADA;
UINT32 bestAnySlice=0;
UINT32 goodThisSlice=0;
UINT32 worstSlice = 0;
UINT32 worstAnySlice=0xDADA;
UINT32 target = (width/2);
UINT32 first, last;
INT32 diff;
UINT32 bestDiff;
UINT32 bestDelay;
if( (scanParameter[0]== BOC_VAR_RXDELAY) && (scanParameter[1]== BOC_VAR_RXTHRESHOLD) ){
newInformation(__FILE__,__LINE__,"Analysis for BOC_RXDELAY / BOC_RXTHRESHOLD");
for(link=0;link<96;link++){
// First: find BOC_RXDELAY slice with smallest number of good clk/2 data points
// Set BOC_RXDELAY to this value plus 9 (to be away from trailing edge);
bin=0;
bestSlice=0; // not actually used unless clk/2 data found
bestAnySlice=0;
goodThisSlice=0;
worstSlice=0;
worstAnySlice=0xDADA;
for(x=0; x<scanSize[0]; x++){ // delay
goodThisSlice = 0;
for(y=scanSize[0]; y<(scanSize[0]+scanSize[1]); y++){ // threshold
if(buffer[( link *nbins)+bin] == target){
goodThisSlice++;
}
bin++;
}
if(goodThisSlice<worstAnySlice){
worstAnySlice = goodThisSlice;
worstSlice = x;
}
if(goodThisSlice>bestAnySlice){
bestAnySlice = goodThisSlice;
}
}
if(bestAnySlice >0){ // link did return some clk/2 data
//bestSlice = worstSlice + 9; // assumes points[x] == x !!!
bestDelay = (points[worstSlice] + 9) % 24; // handle wrapping
// Now find the slice closest to (or equal to) this delay
bestDiff=0xDADA;
for(x=0;x<scanSize[0];x++){
diff = (INT32)points[x] - (INT32)bestDelay;
if(diff == 0){ // works if equal - what if not :-(
bestSlice = x;
break;
}else if (diff<1){
diff = diff * -1;
}
if(diff < bestDiff){
bestDiff = diff;
bestSlice = x;
}
}
last = 0;
first = 0xDADA;
// Within the chosen BOC_RXDELAY slice,
// set BOC_RXTHRESHOLD to be 3/4 of the way between the lower and upper edges
bin = bestSlice * scanSize[1]; // Start of this RXTHRESHOLD slice
for(y=scanSize[0]; y<(scanSize[0]+scanSize[1]); y++){
if(buffer[( link *nbins)+bin] == target){
if(y<first) first = y;
if(y>last) last = y;
}
bin++;
}
best[0] = bestDelay;
best[1] = ((points[first]+points[last])*2)/3; // this is OK!
sprintf(genStr,"link %d setting delay = 0d%02d threshold = 0d%03d\n",
link,best[0],best[1]);
newInformation(__FILE__,__LINE__,genStr);
setBocVariable(link,scanParameter[0],best[0]);
setBocVariable(link,scanParameter[1],best[1]);
}
} //end for(link)
}else{
newInformation(__FILE__,__LINE__,"No analysis available for requested scan");
}
}else{
newInformation(__FILE__,__LINE__,"Algorithm <=0: analysis not requested");
}
// Finish off
primOutput->count = outputLength/2;
// Updating this is enough to get the reply sent
primData->repBodyLength = (outputLength/2)+1;
return returnCode;
}