/** This thread is woken up by an interrupt or a request to read status * It loops calling readFIFO until acquiring_ goes to false. * readFIFO only reads a limited amount of FIFO data at once in order * to avoid blocking the device support threads. */ void drvSIS3801::readFIFOThread() { int count; int signal; int chan; int nChans; int status; int i; bool acquiring; // We have a separate flag because we need to continue processing one last // time even if acquiring_ goes to false because acquisition was manually stopped epicsUInt32 scalerPresets[SIS38XX_MAX_SIGNALS]; epicsUInt32 *pOut=NULL; epicsTimeStamp t1, t2; static const char* functionName="readFIFOThread"; while(true) { epicsEventWait(readFIFOEventId_); // We got an event, which can come from acquisition starting, or FIFO full interrupt asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got readFIFOEvent, eventType=%d, interrupt status=0x%8.8x\n", driverName, functionName, eventType_, registers_->csr_reg & 0xFFF00000); lock(); acquiring = acquiring_; unlock(); while (acquiring) { lock(); for (i=0; i<maxSignals_; i++) getIntegerParam(i, scalerPresets_, (int *)&scalerPresets[i]); getIntegerParam(mcaNumChannels_, &nChans); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: scaler presets[0]=%d, scalerData[0]=%d\n", driverName, functionName, scalerPresets[0], scalerData_[0]); signal = nextSignal_; chan = nextChan_; count = 0; // This block of code can be slow and does not require the asynPortDriver lock because we are not // accessing object data that could change. // It does require the FIFO lock so no one resets the FIFO while it executes epicsMutexLock(fifoLockId_); unlock(); epicsTimeGetCurrent(&t1); /* Read out FIFO. It would be more efficient not to check the empty * flag on each transfer, using the almost empty flag. But this has gotten * too complex, and is unlikely to save time on new boards with lots of * memory. */ if (acquireMode_== ACQUIRE_MODE_MCS) { // Copy the data from the FIFO to the mcsBuffer pOut = mcsData_ + signal*maxChans_ + chan; asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: pOut=%p, signal=%d, chan=%d\n", driverName, functionName, pOut, signal, chan); while (((registers_->csr_reg & STATUS_M_FIFO_FLAG_EMPTY)==0) && (chan < nChans) && acquiring_) { *pOut = registers_->fifo_reg; signal++; count++; if (signal >= maxSignals_) { signal = 0; chan++; pOut = mcsData_ + chan; } else { pOut += maxChans_; } } } else if (acquireMode_ == ACQUIRE_MODE_SCALER) { while ((registers_->csr_reg & STATUS_M_FIFO_FLAG_EMPTY)==0 && acquiring_) { scalerData_[signal] += registers_->fifo_reg; signal++; count++; if (signal >= maxSignals_) { for (i=0; i<maxSignals_; i++) { if ((scalerPresets[i] != 0) && (scalerData_[i] >= scalerPresets[i])) acquiring = false; } asynPrintIO(pasynUserSelf, ASYN_TRACEIO_DRIVER, (const char*)scalerData_, maxSignals_*sizeof(epicsUInt32), "%s:%s:\n", driverName, functionName); if (!acquiring) break; signal = 0; } } } epicsTimeGetCurrent(&t2); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: read FIFO (%d) in %fs, acquiring=%d\n", driverName, functionName, count, epicsTimeDiffInSeconds(&t2, &t1), acquiring_); // Release the FIFO lock, we are done accessing the FIFO epicsMutexUnlock(fifoLockId_); // Take the lock since we are now changing object data lock(); nextChan_ = chan; nextSignal_ = signal; if (acquireMode_ == ACQUIRE_MODE_MCS) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: pOut=%p,signal=%d, chan=%d\n", driverName, functionName, pOut, signal, chan); checkMCSDone(); } else if (acquireMode_ == ACQUIRE_MODE_SCALER) { if (!acquiring) acquiring_ = false; if (!acquiring_) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: scaler done, doing callbacks\n", driverName, functionName); stopScaler(); setIntegerParam(scalerDone_, 1); callParamCallbacks(); } } /* Reenable interrupts in case we were woken up by an interrupt for FIFO almost full */ enableInterrupts(); acquiring = acquiring_; // Release the lock unlock(); // If we are still acquiring then sleep for a short time, but wake up if there is an interrupt if (acquiring) { status = epicsEventWaitWithTimeout(readFIFOEventId_, epicsThreadSleepQuantum()); if (status == epicsEventWaitOK) asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got interrupt in epicsEventWaitWithTimeout, eventType=%d\n", driverName, functionName, eventType_); } } } }
/** This thread is woken up by an interrupt or a request to read status * It loops calling readFIFO until acquiring_ goes to false. * readFIFO only reads a limited amount of FIFO data at once in order * to avoid blocking the device support threads. */ void drvSIS3820::readFIFOThread() { int status; int count; int signal; int chan; int i; bool acquiring; epicsUInt32 *pIn, *pOut; epicsTimeStamp t1, t2, t3; static const char* functionName="readFIFOThread"; while(true) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: waiting for readFIFOEvent\n", driverName, functionName); enableInterrupts(); (void)epicsEventWait(readFIFOEventId_); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got readFIFOEvent, eventType=%d\n", driverName, functionName, eventType_); // We got an event. This can come from: // Acquisition complete in scaler mode // FIFO full in MCS mode // Acquisition start in MCS mode // For scaler mode we just do callbacks lock(); // Do callbacks on acquiring status when acquisition completes if ((acquiring_ == false) && (acquireMode_ == ACQUIRE_MODE_SCALER)) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: doing callbacks on scalerDone\n", driverName, functionName); setIntegerParam(scalerDone_, 1); callParamCallbacks(); unlock(); continue; } acquiring = acquiring_; unlock(); // MCS mode while (acquiring && (acquireMode_ == ACQUIRE_MODE_MCS)) { lock(); signal = nextSignal_; chan = nextChan_; // This block of code can be slow and does not require the asynPortDriver lock because we are not // accessing object data that could change. // It does require the FIFO lock so no one resets the FIFO while it executes epicsMutexLock(fifoLockId_); unlock(); count = registers_->fifo_word_count_reg; if (count > fifoBufferWords_) count = fifoBufferWords_; epicsTimeGetCurrent(&t1); if (useDma_ && (count >= MIN_DMA_TRANSFERS)) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: doing DMA transfer, fifoBuffer=%p, fifoBaseVME_=%p, count=%d\n", driverName, functionName, fifoBuffer_, fifoBaseVME_, count); status = sysDmaFromVme(dmaId_, fifoBuffer_, (int)fifoBaseVME_, VME_AM_EXT_SUP_D64BLT, (count)*sizeof(int), 8); if (status) { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: doing DMA transfer, error calling sysDmaFromVme, status=%d, error=%d, buff=%p, fifoBaseVME_=%p, count=%d\n", driverName, functionName, status, errno, fifoBuffer_, fifoBaseVME_, count); } else { (void)epicsEventWait(dmaDoneEventId_); status = sysDmaStatus(dmaId_); if (status) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: DMA error, errno=%d, message=%s\n", driverName, functionName, errno, strerror(errno)); } } else { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: programmed transfer, count=%d\n", driverName, functionName, count); // Note: we were previously using memcpy here. But that is not guaranteed to do a word transfer, which the // SIS3820 requires for reading the FIFO. In fact if the word count was 1 then a memcpy of 4 bytes was clearly // not doing a word transfer on vxWorks, and was generating bus errors. for (i=0; i<count; i++) fifoBuffer_[i] = fifoBaseCPU_[i]; } epicsTimeGetCurrent(&t2); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: read FIFO (%d) in %fs, fifo word count after=%d, fifoBuffer_=%p, fifoBaseCPU_=%p\n", driverName, functionName, count, epicsTimeDiffInSeconds(&t2, &t1), registers_->fifo_word_count_reg, fifoBuffer_, fifoBaseCPU_); // Release the FIFO lock, we are done accessing the FIFO epicsMutexUnlock(fifoLockId_); // Copy the data from the FIFO buffer to the mcsBuffer pOut = mcsData_ + signal*maxChans_ + chan; pIn = fifoBuffer_; for (i=0; i<count; i++) { *pOut = *pIn++; signal++; if (signal == maxSignals_) { signal = 0; chan++; pOut = mcsData_ + chan; } else { pOut += maxChans_; } } epicsTimeGetCurrent(&t2); // Take the lock since we are now changing object data lock(); nextChan_ = chan; nextSignal_ = signal; asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: copied data to mcsBuffer in %fs, nextChan=%d, nextSignal=%d\n", driverName, functionName, epicsTimeDiffInSeconds(&t3, &t3), nextChan_, nextSignal_); checkMCSDone(); acquiring = acquiring_; /* Re-enable FIFO threshold and FIFO almost full interrupts */ /* NOTE: WE ARE NOT USING FIFO THRESHOLD INTERRUPTS FOR NOW */ /* registers_->irq_control_status_reg = SIS3820_IRQ_SOURCE1_ENABLE; */ registers_->irq_control_status_reg = SIS3820_IRQ_SOURCE4_ENABLE; // Release the lock unlock(); enableInterrupts(); // If we are still acquiring sleep for a short time but wake up on interrupt if (acquiring) { status = epicsEventWaitWithTimeout(readFIFOEventId_, epicsThreadSleepQuantum()); if (status == epicsEventWaitOK) asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got interrupt in epicsEventWaitWithTimeout, eventType=%d\n", driverName, functionName, eventType_); } } } }