/** 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_);
      }
    }  
  }
}
示例#2
0
/** 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_);
      }
    }
  }
}