From 284a92fabe5dcdaa063472400e42ca2d1317e4e2 Mon Sep 17 00:00:00 2001
From: Rolf Bork <rolf.bork@ligo.org>
Date: Fri, 29 Mar 2019 15:27:18 +0000
Subject: [PATCH] Added controller for Cymac using new I/O chassis.

git-svn-id: https://redoubt.ligo-wa.caltech.edu/svn/advLigoRTS/trunk@4935 6dcd42c9-f523-4c6d-aada-af552506706e
---
 src/fe/controllerCymac.c | 1038 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 1038 insertions(+)
 create mode 100644 src/fe/controllerCymac.c

diff --git a/src/fe/controllerCymac.c b/src/fe/controllerCymac.c
new file mode 100644
index 000000000..1d9500423
--- /dev/null
+++ b/src/fe/controllerCymac.c
@@ -0,0 +1,1038 @@
+/*----------------------------------------------------------------------*/
+/*                                                                      */
+/*                      -------------------                             */
+/*                                                                      */
+/*                             LIGO                                     */
+/*                                                                      */
+/*        THE LASER INTERFEROMETER GRAVITATIONAL WAVE OBSERVATORY.      */
+/*                                                                      */
+/*                     (C) The LIGO Project, 2012.                      */
+/*                                                                      */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+
+///	@file controllerIop.c
+///	@brief Main scheduler program for compiled real-time kernal object. \n
+/// 	@detail More information can be found in the following DCC document:
+///<	<a href="https://dcc.ligo.org/cgi-bin/private/DocDB/ShowDocument?docid=7688">T0900607 CDS RT Sequencer Software</a>
+///	@author R.Bork, A.Ivanov
+///     @copyright Copyright (C) 2014 LIGO Project      \n
+///<    California Institute of Technology              \n
+///<    Massachusetts Institute of Technology           \n\n
+///     @license This program is free software: you can redistribute it and/or modify
+///<    it under the terms of the GNU General Public License as published by
+///<    the Free Software Foundation, version 3 of the License.                 \n
+///<    This program is distributed in the hope that it will be useful,
+///<    but WITHOUT ANY WARRANTY; without even the implied warranty of
+///<    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///<    GNU General Public License for more details.
+
+
+#include <linux/version.h>
+#include <linux/init.h>
+#undef printf
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/kthread.h>
+#include <asm/delay.h>
+#include <asm/cacheflush.h>
+
+#include <linux/slab.h>
+/// Can't use printf in kernel module so redefine to use Linux printk function
+#define printf printk
+#include <drv/cdsHardware.h>
+#include "inlineMath.h"
+
+#include </usr/src/linux/arch/x86/include/asm/processor.h>
+#include </usr/src/linux/arch/x86/include/asm/cacheflush.h>
+
+// Code can be run without shutting down CPU by changing this compile flag
+#ifndef NO_CPU_SHUTDOWN
+extern int vprintkl(const char*, va_list);
+extern int printkl(const char*, ...);
+extern long ligo_get_gps_driver_offset(void);
+char fmt1[512];
+int printk(const char *fmt, ...) {
+    va_list args;
+    int r;
+
+    strcat(strcpy(fmt1, SYSTEM_NAME_STRING_LOWER), ": ");
+    strcat(fmt1, fmt);
+    va_start(args, fmt);
+    r = vprintkl(fmt1, args);
+    va_end(args);
+    return r;
+}
+#endif
+
+
+#include "fm10Gen.h"		// CDS filter module defs and C code
+#include "feComms.h"		// Lvea control RFM network defs.
+#include "daqmap.h"		// DAQ network layout
+#include "cds_types.h"
+#include "controller.h"
+
+#ifndef NO_DAQ
+#include "drv/fb.h"
+#include "drv/daqLib.c"         // DAQ/GDS connection software
+#endif
+
+#include "drv/map.h"		// PCI hardware defs
+#include "drv/epicsXfer.c"	// User defined EPICS to/from FE data transfer function
+#include "timing.c"		// timing module / IRIG-B  functions
+
+#include "drv/inputFilterModule.h"		
+#include "drv/inputFilterModule1.h"		
+
+#ifdef DOLPHIN_TEST
+#include "dolphin.c"
+#endif
+
+
+#ifdef TIME_MASTER
+TIMING_SIGNAL *pcieTimer;
+#endif
+
+
+adcInfo_t adcinfo;
+dacInfo_t dacinfo;
+duotone_diag_t dt_diag;
+timing_diag_t timeinfo;
+
+// Contec 64 input bits plus 64 output bits (Standard for aLIGO)
+/// Contec6464 input register values
+unsigned int CDIO6464InputInput[MAX_DIO_MODULES]; // Binary input bits
+/// Contec6464 - Last output request sent to module.
+unsigned int CDIO6464LastOutState[MAX_DIO_MODULES]; // Current requested value of the BO bits
+/// Contec6464 values to be written to the output register
+unsigned int CDIO6464Output[MAX_DIO_MODULES]; // Binary output bits
+
+// This Contect 16 input / 16 output DIO card is used to control timing slave by IOP
+/// Contec1616 input register values
+unsigned int CDIO1616InputInput[MAX_DIO_MODULES]; // Binary input bits
+/// Contec1616 output register values read back from the module
+unsigned int CDIO1616Input[MAX_DIO_MODULES]; // Current value of the BO bits
+/// Contec1616 values to be written to the output register
+unsigned int CDIO1616Output[MAX_DIO_MODULES]; // Binary output bits
+/// Holds ID number of Contec1616 DIO card(s) used for timing control.
+int tdsControl[3];	// Up to 3 timing control modules allowed in case I/O chassis are daisy chained
+/// Total number of timing control modules found on bus
+int tdsCount = 0;
+
+
+/// Maintains present cycle count within a one second period.
+int cycleNum = 0;
+int adcCycleNum = 0;
+unsigned int odcStateWord = 0xffff;
+/// Value of readback from DAC FIFO size registers; used in diags for FIFO overflow/underflow.
+int out_buf_size = 0; // test checking DAC buffer size
+unsigned int cycle_gps_time = 0; // Time at which ADCs triggered
+unsigned int cycle_gps_event_time = 0; // Time at which ADCs triggered
+unsigned int   cycle_gps_ns = 0;
+unsigned int   cycle_gps_event_ns = 0;
+unsigned int   gps_receiver_locked = 0; // Lock/unlock flag for GPS time card
+/// GPS time in GPS seconds
+unsigned int timeSec = 0;
+unsigned int timeSecDiag = 0;
+/* 1 - error occured on shmem; 2 - RFM; 3 - Dolphin */
+unsigned int ipcErrBits = 0;
+int cardCountErr = 0;
+
+int ioClockDac = DAC_PRELOAD_CNT;
+int ioMemCntr = 0;
+int ioMemCntrDac = DAC_PRELOAD_CNT;
+int dac_out = 0;			/// @param dac_out Integer value sent to DAC FIFO
+int dacEnable = 0;
+int pBits[9] = {1,2,4,8,16,32,64,128,256};	/// @param pBits[] Lookup table for quick power of 2 calcs
+int dacOF[MAX_DAC_MODULES];		/// @param dacOF[]  DAC overrange counters
+int dacWriteEnable = 0;	/// @param dacWriteEnable  No DAC outputs until >4 times through code
+int dacTimingErrorPending[MAX_DAC_MODULES];
+static int dacTimingError = 0;
+
+struct rmIpcStr *daqPtr;
+int dacWatchDog = 0;
+
+int  getGpsTime(unsigned int *tsyncSec, unsigned int *tsyncUsec); 
+
+// Include C code modules
+#include "moduleLoadIop.c"
+#include "map.c"
+#include <drv/iop_adc_functions.c>
+#include <drv/iop_dac_functions.c>
+#include <drv/adc_info.c>
+#include <drv/dac_info.c>
+
+
+char daqArea[2*DAQ_DCU_SIZE];		// Space allocation for daqLib buffers
+int cpuId = 1;
+
+#ifdef DUAL_DAQ_DC
+	#define MX_OK	15
+#else
+	#define MX_OK	3
+#endif
+
+// Whether run on internal timer (when no ADC cards found)
+int run_on_timer = 0;
+
+// Initial diag reset flag
+int initialDiagReset = 1;
+
+// Cache flushing mumbo jumbo suggested by Thomas Gleixner, it is probably useless
+// Did not see any effect
+char fp [64*1024];
+
+
+//***********************************************************************
+// TASK: fe_start()	
+// This routine is the skeleton for all front end code	
+//***********************************************************************
+/// This function is the main real-time sequencer or scheduler for all code built
+/// using the RCG. \n
+/// There are two primary modes of operation, based on two compile options: \n
+///	- ADC_MASTER: Software is compiled as an I/O Processor (IOP). 
+///	- ADC_SLAVE: Normal user control process.
+/// This code runs in a continuous loop at the rate specified in the RCG model. The
+/// loop is synchronized and triggered by the arrival of ADC data, the ADC module in turn
+/// is triggered to sample by the 64KHz clock provided by the Timing Distribution System.
+///	- 
+void *fe_start(void *arg)
+{
+  int tempClock[4];
+  int ii,jj,kk,ll;			// Dummy loop counter variables
+  // int mm;
+  static int clock1Min = 0;		///  @param clockMin Minute counter (Not Used??)
+  static int cpuClock[CPU_TIMER_CNT];	///  @param cpuClock[] Code timing diag variables
+
+
+  					///< Code runs longer for first few cycles on startup as it settles in,
+					///< so this helps prevent long cycles during that time.
+  // int limit = OVERFLOW_LIMIT_16BIT;      /// @param limit ADC/DAC overflow test value
+  // int mask = GSAI_DATA_MASK;            /// @param mask Bit mask for ADC/DAC read/writes
+  // int num_outs = MAX_DAC_CHN_PER_MOD;   /// @param num_outs Number of DAC channels variable
+  // volatile unsigned int *pDacData;	/// @param *pDacData Pointer to DAC PCI data space
+  // int dacEnable = 0;
+  // int pBits[9] = {1,2,4,8,16,32,64,128,256};	/// @param pBits[] Lookup table for quick power of 2 calcs
+  int sync21ppsCycles = 0;		/// @param sync32ppsCycles Number of attempts to sync to 1PPS
+  // int dkiTrip = 0;
+  RFM_FE_COMMS *pEpicsComms;		/// @param *pEpicsComms Pointer to EPICS shared memory space
+  int myGmError2 = 0;			/// @param myGmError2 Myrinet error variable
+  int status;				/// @param status Typical function return value
+  float onePps;				/// @param onePps Value of 1PPS signal, if used, for diagnostics
+  int onePpsHi = 0;			/// @param onePpsHi One PPS diagnostic check
+  int onePpsTime = 0;			/// @param onePpsTime One PPS diagnostic check
+#ifdef DIAG_TEST
+  float onePpsTest;			/// @param onePpsTest Value of 1PPS signal, if used, for diagnostics
+  int onePpsHiTest[10];			/// @param onePpsHiTest[] One PPS diagnostic check
+  int onePpsTimeTest[10];		/// @param onePpsTimeTest[] One PPS diagnostic check
+#endif
+  int dcuId;				/// @param dcuId DAQ ID number for this process
+  static int missedCycle = 0;		/// @param missedCycle Incremented error counter when too many values in ADC FIFO
+  int diagWord = 0;			/// @param diagWord Code diagnostic bit pattern returned to EPICS
+  int system = 0;
+  int sync21pps = 0;			/// @param sync21pps Code startup sync to 1PPS flag
+  int syncSource = SYNC_SRC_NONE;	/// @param syncSource Code startup synchronization source
+  int mxStat = 0;			/// @param mxStat Net diags when myrinet express is used
+  int mxDiag = 0;
+  int mxDiagR = 0;
+
+  int feStatus = 0;
+  int dkiTrip = 0;
+  int adcInAlarm = 0;
+  int adcMissedCycle = 0;
+  int dac_restore = 92160;
+
+  // volatile GSA_18BIT_DAC_REG *dac18bitPtr;	// Pointer to 16bit DAC memory area
+  // volatile GSA_20BIT_DAC_REG *dac20bitPtr;  // Pointer to 20bit DAC memory area
+  // volatile GSC_DAC_REG *dac16bitPtr;		// Pointer to 18bit DAC memory area
+  unsigned int usec = 0;
+
+
+  unsigned long cpc;
+  float duotoneTimeDac;
+  float duotoneTime;
+
+
+/// **********************************************************************************************\n
+/// Start Initialization Process \n
+/// **********************************************************************************************\n
+  memset (tempClock, 0, sizeof(tempClock));
+
+  /// \> Flush L1 cache
+  memset (fp, 0, 64*1024);
+  memset (fp, 1, 64*1024);
+  clflush_cache_range ((void *)fp, 64*1024);
+
+  fz_daz(); /// \> Kill the denorms!
+
+  /// \> Init comms with EPICS processor */
+  pEpicsComms = (RFM_FE_COMMS *)_epics_shm;
+  pLocalEpics = (CDS_EPICS *)&pEpicsComms->epicsSpace;
+  pEpicsDaq = (char *)&(pLocalEpics->epicsOutput);
+
+adcInfo_t *padcinfo = (adcInfo_t *)&adcinfo;
+#ifdef OVERSAMPLE
+  /// \> Zero out filter histories
+  memset(dHistory, 0, sizeof(dHistory));
+  memset(dDacHistory, 0, sizeof(dDacHistory));
+#endif
+
+  /// \> Set pointers to filter module data buffers. \n
+  /// - ---- Prior to V2.8, separate local/shared memories for FILT_MOD data.\n
+  /// - ---- V2.8 and later, code uses EPICS shared memory only. This was done to: \n
+  /// - -------- Allow daqLib.c to retrieve filter module data directly from shared memory. \n
+  /// - -------- Avoid copy of filter module data between to memory locations, which was slow. \n
+  pDsp[system] = (FILT_MOD *)(&pEpicsComms->dspSpace);
+  pCoeff[system] = (VME_COEF *)(&pEpicsComms->coeffSpace);
+  dspPtr[system] = (FILT_MOD *)(&pEpicsComms->dspSpace);
+
+  /// \> Clear the FE reset which comes from Epics
+  pLocalEpics->epicsInput.vmeReset = 0;
+
+  // Clear input masks
+  pLocalEpics->epicsInput.burtRestore_mask = 0;
+  pLocalEpics->epicsInput.dacDuoSet_mask = 0;
+
+/// \> Init code synchronization source.
+  // Look for DIO card or IRIG-B Card
+  // if Contec 1616 BIO present, TDS slave will be used for timing.
+  if(cdsPciModules.cDio1616lCount) syncSource = SYNC_SRC_TDS;
+  else syncSource = SYNC_SRC_1PPS;
+  syncSource = SYNC_SRC_NONE;
+
+
+#ifdef TIME_MASTER
+  pcieTimer = (TIMING_SIGNAL *) ((volatile char *)(cdsPciModules.dolphinWrite[0]) + IPC_PCIE_TIME_OFFSET);
+// printf("I am a TIMING MASTER **************\n");
+#endif
+
+/// < Read in all Filter Module EPICS coeff settings
+  for(ii=0;ii<MAX_MODULES;ii++)
+  {
+    checkFiltReset(ii, dspPtr[0], pDsp[0], &dspCoeff[0], MAX_MODULES, pCoeff[0]);
+  }
+
+  // Need this FE dcuId to make connection to FB
+  dcuId = pLocalEpics->epicsInput.dcuId;
+  pLocalEpics->epicsOutput.dcuId = dcuId;
+
+  // Reset timing diagnostics
+  pLocalEpics->epicsOutput.diagWord = 0;
+  pLocalEpics->epicsOutput.timeDiag = 0;
+  pLocalEpics->epicsOutput.timeErr = syncSource;
+
+/// \> Init IIR filter banks
+//   Initialize filter banks  *********************************************
+  for (system = 0; system < NUM_SYSTEMS; system++) {
+    for(ii=0;ii<MAX_MODULES;ii++){
+      for(jj=0;jj<FILTERS;jj++){
+        for(kk=0;kk<MAX_COEFFS;kk++){
+          dspCoeff[system].coeffs[ii].filtCoeff[jj][kk] = 0.0;
+        }
+        dspCoeff[system].coeffs[ii].filtSections[jj] = 0;
+      }
+    }
+  }
+
+  /// \> Initialize all filter module excitation signals to zero 
+  for (system = 0; system < NUM_SYSTEMS; system++)
+    for(ii=0;ii<MAX_MODULES;ii++)
+       pDsp[0]->data[ii].exciteInput = 0.0;
+
+
+  /// \> Initialize IIR filter bank values
+  if (initVars(pDsp[0], pDsp[0], dspCoeff, MAX_MODULES, pCoeff[0])) {
+    pLocalEpics->epicsOutput.fe_status = FILT_INIT_ERROR;
+    return 0;
+  }
+
+
+  udelay(1000);
+
+/// \> Initialize DAQ variable/software 
+#if !defined(NO_DAQ) && !defined(IOP_TASK)
+  /// - ---- Set data range limits for daqLib routine 
+  daq.filtExMin = GDS_16K_EXC_MIN;
+  daq.filtTpMin = GDS_16K_TP_MIN;
+  daq.filtExMax = daq.filtExMin + MAX_MODULES;
+  daq.filtExSize = MAX_MODULES;
+  daq.xExMin = daq.filtExMax;
+  daq.xExMax = daq.xExMin + GDS_MAX_NFM_EXC;
+  daq.filtTpMax = daq.filtTpMin + MAX_MODULES * 3;
+  daq.filtTpSize = MAX_MODULES * 3;
+  daq.xTpMin = daq.filtTpMax;
+  daq.xTpMax = daq.xTpMin + GDS_MAX_NFM_TP;
+  
+  /// - ---- Initialize DAQ function
+  status = daqWrite(0,dcuId,daq,DAQ_RATE,testpoint,dspPtr[0],0, (int *)(pLocalEpics->epicsOutput.gdsMon),xExc,pEpicsDaq);
+  if(status == -1) 
+  {
+    pLocalEpics->epicsOutput.fe_status = DAQ_INIT_ERROR;
+    vmeDone = 1;
+    return(0);
+  }
+
+#endif
+
+  /// - ---- Assign DAC testpoint pointers
+  for (ii = 0; ii <  cdsPciModules.dacCount; ii++)
+    for (jj = 0; jj < MAX_DAC_CHN_PER_MOD; jj++) // 16 per DAC regardless of the actual
+        testpoint[MAX_DAC_CHN_PER_MOD * ii + jj] = floatDacOut + MAX_DAC_CHN_PER_MOD * ii + jj;
+
+  // Zero out storage
+  memset(floatDacOut, 0, sizeof(floatDacOut));
+
+  pLocalEpics->epicsOutput.ipcStat = 0;
+  pLocalEpics->epicsOutput.fbNetStat = 0;
+  pLocalEpics->epicsOutput.tpCnt = 0;
+
+  // Clear the code exit flag
+  vmeDone = 0;
+
+  /// \> Call user application software initialization routine.
+  // printf("Calling feCode() to initialize\n");
+  iopDacEnable = feCode(cycleNum,dWord,dacOut,dspPtr[0],&dspCoeff[0], (struct CDS_EPICS *)pLocalEpics,1);
+
+  // Initialize timing info variables
+  timeinfo.cpuTimeEverMax = 0;		
+  timeinfo.cpuTimeEverMaxWhen = 0;	
+  timeinfo.startGpsTime = 0;
+  timeinfo.usrHoldTime = 0;		
+  timeinfo.timeHold = 0;		
+  timeinfo.timeHoldHold = 0;		
+  timeinfo.timeHoldWhen = 0;		
+  timeinfo.timeHoldWhenHold = 0;		
+  timeinfo.usrTime = 0;		
+  timeinfo.cycleTime = 0;		
+  missedCycle = 0;
+
+  // Initialize duotone measurement signals
+  for(ii=0;ii<IOP_IO_RATE;ii++) {
+    dt_diag.adc[ii] = 0;
+    dt_diag.dac[ii] = 0;
+  }
+  dt_diag.totalAdc = 0.0;
+  dt_diag.totalDac = 0.0;
+  dt_diag.meanAdc = 0.0;
+  dt_diag.meanDac = 0.0;
+  dt_diag.dacDuoEnable = 0.0;
+
+  /// \> Initialize the ADC modules *************************************
+  pLocalEpics->epicsOutput.fe_status = INIT_ADC_MODS;
+  status = iop_adc_init(padcinfo);
+
+  /// \> Initialize the DAC module variables  **********************************
+  pLocalEpics->epicsOutput.fe_status = INIT_DAC_MODS;
+  status = iop_dac_init(dacTimingErrorPending);
+
+  pLocalEpics->epicsOutput.fe_status = INIT_SYNC;
+
+/// \> Find the code syncrhonization source. \n
+/// - Standard aLIGO Sync source is the Timing Distribution System (TDS) (SYNC_SRC_TDS). 
+  if (!run_on_timer) {
+  switch(syncSource)
+  {
+    /// \>\> For SYNC_SRC_TDS, initialize system for synchronous start on 1PPS mark:
+    case SYNC_SRC_TDS:
+    case SYNC_SRC_NONE:
+      /// - ---- Turn off TDS slave unit timing clocks, in turn removing clocks from ADC/DAC modules.
+      for(ii=0;ii<tdsCount;ii++)
+      {
+        CDIO1616Output[ii] = TDS_STOP_CLOCKS;
+        CDIO1616Input[ii] = contec1616WriteOutputRegister(&cdsPciModules, tdsControl[ii], CDIO1616Output[ii]);
+      }
+      udelay(MAX_UDELAY);
+      udelay(MAX_UDELAY);
+      /// - ---- Arm ADC modules
+      gsc16ai64Enable(cdsPciModules.adcCount);
+      /// - ----  Arm DAC outputs
+      gsc18ao8Enable(&cdsPciModules);
+      gsc16ao16Enable(&cdsPciModules);
+      // Set synched flag so later code will not check for 1PPS
+      sync21pps = 1;
+      udelay(MAX_UDELAY);
+      udelay(MAX_UDELAY);
+      /// - ---- Preload DAC FIFOS\n
+      /// - --------- Code runs intrinsically slower first few cycle after startup, so new DAC
+      /// values not written until a few cycle into run. \n
+      /// - --------- DAC timing diags will later check FIFO sizes to verify synchrounous timing.
+      #ifndef NO_DAC_PRELOAD
+      status = iop_dac_preload(dacPtr);
+      #endif
+      /// - ---- Start the timing clocks\n
+      /// - --------- Send start command to TDS slave.\n
+      /// - --------- TDS slave will begin sending 64KHz clocks synchronous to next 1PPS mark.
+      // CDIO1616Output[tdsControl] = 0x7B00000;
+      for(ii=0;ii<tdsCount;ii++)
+      {
+        // CDIO1616Output[ii] = TDS_START_ADC_NEG_DAC_POS;
+        CDIO1616Output[ii] = TDS_START_ADC_NEG_DAC_POS | TDS_NO_DAC_DUOTONE;
+        CDIO1616Input[ii] = contec1616WriteOutputRegister(&cdsPciModules, tdsControl[ii], CDIO1616Output[ii]);
+      }
+      break;
+    case SYNC_SRC_1PPS:
+#ifndef NO_DAC_PRELOAD
+      status = iop_dac_preload(dacPtr);
+#endif
+      // Arm ADC modules
+      // This has to be done sequentially, one at a time.
+      status = sync_adc_2_1pps();
+      break;
+    default: {
+      // IRIG-B card not found, so use CPU time to get close to 1PPS on startup
+      // Pause until this second ends
+      break;
+    }
+  }
+  }
+
+
+  pLocalEpics->epicsOutput.fe_status = NORMAL_RUN;
+
+  onePpsTime = cycleNum;
+#ifdef REMOTE_GPS
+  timeSec = remote_time((struct CDS_EPICS *)pLocalEpics);
+  printf ("Using remote GPS time %d \n",timeSec);
+#else
+  timeSec = current_time() -1;
+#endif
+
+  rdtscl(adcinfo.adcTime);
+
+  /// ******************************************************************************\n
+  /// Enter the infinite FE control loop  ******************************************\n
+
+  /// ******************************************************************************\n
+  // Calculate how many CPU cycles per code cycle
+  cpc = cpu_khz * 1000;
+  cpc /= CYCLE_PER_SECOND;
+#ifdef NO_CPU_SHUTDOWN
+	usleep_range(2,4);
+#endif
+
+
+#ifdef NO_CPU_SHUTDOWN
+  while(!kthread_should_stop()){
+#else
+  while(!vmeDone){ 	// Run forever until user hits reset
+#endif
+// *****************************************************************************************
+// NORMAL OPERATION -- Wait for ADC data ready
+// *****************************************************************************************
+#ifndef RFM_DIRECT_READ
+/// \> If IOP and RFM DMA selected, block transfer data from GeFanuc RFM cards.
+// Used in block transfers of data from GEFANUC RFM
+/// - ---- Want to start the DMA ASAP, before ADC data starts coming in.
+/// - ----  Note that data only xferred every 4th cycle of IOP, so max data rate on RFM is 16K.
+    if((cycleNum % 4) == 0)
+    {
+      if (cdsPciModules.pci_rfm[0]) vmic5565DMA(&cdsPciModules,0,(cycleNum % IPC_BLOCKS));
+      if (cdsPciModules.pci_rfm[1]) vmic5565DMA(&cdsPciModules,1,(cycleNum % IPC_BLOCKS));
+    }
+#endif
+
+/// \> On 1PPS mark \n
+    if(cycleNum == 0)
+    {
+      /// - ---- Check awgtpman status.
+      pLocalEpics->epicsOutput.awgStat = (pEpicsComms->padSpace.awgtpman_gps != timeSec);
+      if(pLocalEpics->epicsOutput.awgStat) feStatus |= FE_ERROR_AWG;
+      /// - ---- Check if DAC outputs are enabled, report error.
+      if(!iopDacEnable || dkiTrip) feStatus |= FE_ERROR_DAC_ENABLE;
+
+      /// - ---- If IOP, Increment GPS second
+      timeSec ++;
+      pLocalEpics->epicsOutput.timeDiag = timeSec;
+      if (cycle_gps_time == 0) {
+        timeinfo.startGpsTime = timeSec;
+        pLocalEpics->epicsOutput.startgpstime = timeinfo.startGpsTime;
+      }
+      cycle_gps_time = timeSec;
+    }
+#ifdef NO_CPU_SHUTDOWN
+    if((cycleNum % 2048) == 0) usleep_range(2,4);
+#endif
+// Start of ADC Read **********************************************************************
+    // Read ADC data
+    status = iop_adc_read (padcinfo, cpuClock);
+    if(status == ADC_BUS_DELAY && dacWriteEnable > 8) {
+      printf("ADC long cycle \n");
+      adcInAlarm = 10;
+      adcMissedCycle = 0;
+      status = gsc16ao16ClearDacBuffer(0);
+      status = iop_dac_recover(1,dacPtr);
+    }
+    if(status == ADC_SHORT_CYCLE && adcInAlarm) adcMissedCycle ++;
+    if(adcInAlarm > 1) adcInAlarm --;
+    if(status == 0 && adcInAlarm == 1) {
+      printf("IOP missed %d ADC clocks\n",adcMissedCycle);
+      adcInAlarm = 0;
+      dac_restore = adcMissedCycle;
+    }
+
+    // Try synching to 1PPS on ADC[0][31] if not using IRIG-B or TDS
+    // Only try for 1 sec.
+    if(!sync21pps)
+    {
+      // 1PPS signal should rise above 4000 ADC counts if present.
+      if((adcinfo.adcData[0][31] < ONE_PPS_THRESH) && (sync21ppsCycles < (CYCLE_PER_SECOND*OVERSAMPLE_TIMES))) 
+      {
+        ll = -1;
+        sync21ppsCycles ++;
+      }else {
+        // Need to start clocking the DAC outputs.
+        gsc18ao8Enable(&cdsPciModules);
+	gsc16ao16Enable(&cdsPciModules);
+        sync21pps = 1;
+	// 1PPS never found, so indicate NO SYNC to user
+	if(sync21ppsCycles >= (CYCLE_PER_SECOND*OVERSAMPLE_TIMES))
+	{
+          syncSource = SYNC_SRC_NONE;
+        } else {
+          // 1PPS found and synched to
+          syncSource = SYNC_SRC_1PPS;
+        }
+        pLocalEpics->epicsOutput.timeErr = syncSource;
+      }
+    }
+
+
+
+
+// **************************************************************************************
+/// \> Call the front end specific application  ******************\n
+/// - -- This is where the user application produced by RCG gets called and executed. \n\n
+    rdtscl(cpuClock[CPU_TIME_USR_START]);
+    iopDacEnable = feCode(cycleNum,dWord,dacOut,dspPtr[0],&dspCoeff[0],(struct CDS_EPICS *)pLocalEpics,0);
+    rdtscl(cpuClock[CPU_TIME_USR_END]);
+// **************************************************************************************
+//
+    /// - ---- Reset ADC DMA Start Flag \n
+    /// - --------- This allows ADC to dump next data set whenever it is ready
+    for(jj=0;jj<cdsPciModules.adcCount;jj++) gsc16ai64DmaEnable(jj);
+    odcStateWord = 0;
+
+// ********************************************************************
+/// WRITE DAC OUTPUTS ***************************************** \n
+// ********************************************************************
+
+    // COMMENT OUT NEX LINE FOR TEST STAND w/bad DAC cards. 
+#ifndef DAC_WD_OVERRIDE
+    // If a DAC module has bad timing then quit writing outputs
+    // if(dacTimingError) iopDacEnable = 0;
+#endif
+    // Write out data to DAC modules
+    if(dac_restore == 4) {
+      status = gsc16ao16ClearDacBuffer(0);
+      status = iop_dac_recover(8,dacPtr);
+      printf("Restored DAC \n");
+    }
+    dkiTrip = 0;
+    if(dac_restore > 4) dacWriteEnable = 0;
+    dkiTrip = iop_dac_write();
+    if(dac_restore) dac_restore --;
+
+
+// ***********************************************************************
+/// BEGIN HOUSEKEEPING ************************************************ \n
+// ***********************************************************************
+
+    pLocalEpics->epicsOutput.cycle = cycleNum;
+
+// *****************************************************************
+/// \> Cycle 1 and Spectricom IRIGB (standard), get IRIG-B time information.
+// *****************************************************************
+    if(cycleNum == HKP_READ_TSYNC_IRIBB)
+    {
+        pLocalEpics->epicsOutput.irigbTime = 15;
+    }
+
+// *****************************************************************
+/// \> Cycle 16, perform duotone diag calcs.
+// *****************************************************************
+    if(cycleNum == HKP_DT_CALC)
+    {
+      pLocalEpics->epicsOutput.dtTime = 5;
+      pLocalEpics->epicsOutput.dacDtTime = 89;
+    }
+
+// *****************************************************************
+/// \> Cycle 18, Send timing info to EPICS at 1Hz
+// *****************************************************************
+    if(cycleNum ==HKP_TIMING_UPDATES)	
+    {
+      pLocalEpics->epicsOutput.cpuMeter = timeinfo.timeHold;
+      pLocalEpics->epicsOutput.cpuMeterMax = timeinfo.timeHoldMax;
+      pLocalEpics->epicsOutput.dacEnable = dacEnable;
+      timeinfo.timeHoldHold = timeinfo.timeHold;
+      timeinfo.timeHold = 0;
+      timeinfo.timeHoldWhenHold = timeinfo.timeHoldWhen;
+
+      if (timeSec % 4 == 0) pLocalEpics->epicsOutput.adcWaitTime = 
+        adcinfo.adcHoldTimeMin;
+      else if (timeSec % 4 == 1)
+        pLocalEpics->epicsOutput.adcWaitTime =  adcinfo.adcHoldTimeMax;
+      else
+        pLocalEpics->epicsOutput.adcWaitTime = adcinfo.adcHoldTimeAvg/CYCLE_PER_SECOND;
+
+      adcinfo.adcHoldTimeAvgPerSec = adcinfo.adcHoldTimeAvg/CYCLE_PER_SECOND;
+      adcinfo.adcHoldTimeMax = 0;
+      adcinfo.adcHoldTimeMin = 0xffff;
+      adcinfo.adcHoldTimeAvg = 0;
+      if((adcinfo.adcHoldTime > CYCLE_TIME_ALRM_HI) || (adcinfo.adcHoldTime < CYCLE_TIME_ALRM_LO)) 
+      {
+        diagWord |= FE_ADC_HOLD_ERR;
+        feStatus |= FE_ERROR_TIMING;
+      }
+      if(timeinfo.timeHoldMax > CYCLE_TIME_ALRM) 
+      {
+        diagWord |= FE_PROC_TIME_ERR;
+        feStatus |= FE_ERROR_TIMING;
+      }
+      pLocalEpics->epicsOutput.stateWord = feStatus;
+      feStatus = 0;
+      if(pLocalEpics->epicsInput.diagReset || initialDiagReset)
+      {
+        initialDiagReset = 0;
+        pLocalEpics->epicsInput.diagReset = 0;
+        pLocalEpics->epicsInput.ipcDiagReset = 1;
+  	// pLocalEpics->epicsOutput.diags[1] = 0;
+        timeinfo.timeHoldMax = 0;
+        diagWord = 0;
+        ipcErrBits = 0;
+		
+      	for(jj=0;jj<cdsPciModules.adcCount;jj++) adcinfo.adcRdTimeMax[jj] = 0;
+      }
+      pLocalEpics->epicsOutput.diagWord = diagWord;
+      for(jj=0;jj<cdsPciModules.adcCount;jj++) {
+        if(adcinfo.adcRdTimeErr[jj] > MAX_ADC_WAIT_ERR_SEC)
+          pLocalEpics->epicsOutput.stateWord |= FE_ERROR_ADC;
+          adcinfo.adcRdTimeErr[jj] = 0;
+      }
+    }
+
+
+// *****************************************************************
+/// \> Check for requests for filter module clear history requests. 
+/// This is spread out over a number of cycles.
+// Spread out filter coeff update, but keep updates at 16 Hz
+// here we are rounding up:
+//   x/y rounded up equals (x + y - 1) / y
+//
+// *****************************************************************
+    { 
+      static const unsigned int mpc = (MAX_MODULES + (FE_RATE / 16) - 1) / (FE_RATE / 16); // Modules per cycle
+      unsigned int smpc = mpc * subcycle; // Start module counter
+      unsigned int empc = smpc + mpc; // End module counter
+      unsigned int i;
+      for (i = smpc; i < MAX_MODULES && i < empc ; i++) 
+        checkFiltReset(i, dspPtr[0], pDsp[0], &dspCoeff[0], MAX_MODULES, pCoeff[0]);
+    }
+
+// *****************************************************************
+    /// \> Check if code exit is requested
+    if(cycleNum == MAX_MODULES) 
+      vmeDone = stop_working_threads | checkEpicsReset(cycleNum, (struct CDS_EPICS *)pLocalEpics);
+// *****************************************************************
+	// If synced to 1PPS on startup, continue to check that code
+	// is still in sync with 1PPS.
+	// This is NOT normal aLIGO mode.
+    if(syncSource == SYNC_SRC_1PPS)
+    {
+      // Assign chan 32 to onePps 
+      onePps = adcinfo.adcData[ADC_DUOTONE_BRD][ADC_DUOTONE_CHAN];
+      if((onePps > ONE_PPS_THRESH) && (onePpsHi == 0))  
+      {
+        onePpsTime = cycleNum;
+        onePpsHi = 1;
+      }
+      if(onePps < ONE_PPS_THRESH) onePpsHi = 0;  
+
+      // Check if front end continues to be in sync with 1pps
+      // If not, set sync error flag
+      if(onePpsTime > 1) pLocalEpics->epicsOutput.timeErr |= TIME_ERR_1PPS;
+    }
+#ifdef DIAG_TEST
+    for(ii=0;ii<10;ii++)
+    {
+      if(ii<5) onePpsTest = adcinfo.adcData[0][ii];
+      else onePpsTest = adcinfo.adcData[1][(ii-5)];
+      if((onePpsTest > 400) && (onePpsHiTest[ii] == 0))  
+      {
+        onePpsTimeTest[ii] = cycleNum;
+        onePpsHiTest[ii] = 1;
+        if((ii == 0) || (ii == 5)) pLocalEpics->epicsOutput.timingTest[ii] = cycleNum * 15.26;
+        // Slaves do not see 1pps until after IOP signal loops around and back into ADC channel 0,
+        // therefore, need to subtract IOP loop time.
+        else pLocalEpics->epicsOutput.timingTest[ii] = (cycleNum * 15.26) - pLocalEpics->epicsOutput.timingTest[0];
+      }
+      // Reset the diagnostic for next cycle
+      if(cycleNum > 2000) onePpsHiTest[ii] = 0;  
+    }
+#endif
+
+// *****************************************************************
+/// \>  Write data to DAQ.
+// *****************************************************************
+#ifndef NO_DAQ
+		
+    // Call daqLib
+    pLocalEpics->epicsOutput.daqByteCnt = 
+    daqWrite(1,dcuId,daq,DAQ_RATE,testpoint,dspPtr[0],myGmError2,(int *)(pLocalEpics->epicsOutput.gdsMon),xExc,pEpicsDaq);
+    // Send the current DAQ block size to the awgtpman for TP number checking
+    pEpicsComms->padSpace.feDaqBlockSize = curDaqBlockSize;
+    pLocalEpics->epicsOutput.tpCnt = tpPtr->count & 0xff;
+    feStatus |= (FE_ERROR_EXC_SET & tpPtr->count);
+    if (FE_ERROR_EXC_SET & tpPtr->count) odcStateWord |= ODC_EXC_SET;
+    else odcStateWord &= ~(ODC_EXC_SET);
+    if(pLocalEpics->epicsOutput.daqByteCnt > DAQ_DCU_RATE_WARNING) 
+      feStatus |= FE_ERROR_DAQ;
+#endif
+// *****************************************************************
+
+/// \> Cycle 19, write updated diag info to EPICS
+    if(cycleNum == HKP_DIAG_UPDATES)	
+    {
+      pLocalEpics->epicsOutput.userTime = timeinfo.usrHoldTime;
+      pLocalEpics->epicsOutput.ipcStat = ipcErrBits;
+      if(ipcErrBits & 0xf) feStatus |= FE_ERROR_IPC;
+      // Create FB status word for return to EPICS
+      mxStat = 0;
+      mxDiagR = daqPtr->reqAck;
+      if((mxDiag & 1) != (mxDiagR & 1)) mxStat = 1;
+      if((mxDiag & 2) != (mxDiagR & 2)) mxStat += 2;
+#ifdef DUAL_DAQ_DC
+      if((mxDiag & 4) != (mxDiagR & 4)) mxStat += 4;
+      if((mxDiag & 8) != (mxDiagR & 8)) mxStat += 8;
+#endif
+      pLocalEpics->epicsOutput.fbNetStat = mxStat;
+      mxDiag = mxDiagR;
+      if(mxStat != MX_OK) feStatus |= FE_ERROR_DAQ;;
+      timeinfo.usrHoldTime = 0;
+      if(pLocalEpics->epicsInput.overflowReset)
+      {
+        if (pLocalEpics->epicsInput.overflowReset) {
+          for (ii = 0; ii < 16; ii++) {
+            for (jj = 0; jj < cdsPciModules.adcCount; jj++) {
+              adcinfo.overflowAdc[jj][ii] = 0;
+              adcinfo.overflowAdc[jj][ii + 16] = 0;
+              pLocalEpics->epicsOutput.overflowAdcAcc[jj][ii] = 0;
+              pLocalEpics->epicsOutput.overflowAdcAcc[jj][ii + 16] = 0;
+            }
+            for (jj = 0; jj < cdsPciModules.dacCount; jj++) {
+              pLocalEpics->epicsOutput.overflowDacAcc[jj][ii] = 0;
+            }
+          }
+        }
+      }
+      if((pLocalEpics->epicsInput.overflowReset) || (overflowAcc > OVERFLOW_CNTR_LIMIT))
+      {
+        pLocalEpics->epicsInput.overflowReset = 0;
+        pLocalEpics->epicsOutput.ovAccum = 0;
+        overflowAcc = 0;
+      }
+    }
+
+/// \> Cycle 20, Update latest DAC output values to EPICS
+    if(subcycle == HKP_DAC_EPICS_UPDATES)
+    {
+      // Send DAC output values at 16Hzfb
+      for(jj=0;jj<cdsPciModules.dacCount;jj++)
+      {
+        for(ii=0;ii<MAX_DAC_CHN_PER_MOD;ii++)
+        {
+          pLocalEpics->epicsOutput.dacValue[jj][ii] = dacOutEpics[jj][ii];
+        }	
+      }
+    }
+
+// *****************************************************************
+/// \> Cycle 21, Update ADC/DAC status to EPICS.
+// *****************************************************************
+    if(cycleNum == HKP_ADC_DAC_STAT_UPDATES)
+    {
+      pLocalEpics->epicsOutput.ovAccum = overflowAcc;
+      feStatus |= adc_status_update(&adcinfo);
+      feStatus |= dac_status_update(&dacinfo); 
+      // If ADC channels not where they should be, we have no option but to exit
+      // from the RT code ie loops would be working with wrong input data.
+      if (adcinfo.chanHop) {
+        pLocalEpics->epicsOutput.stateWord = FE_ERROR_ADC;
+        stop_working_threads = 1;
+        vmeDone = 1;
+        pLocalEpics->epicsOutput.fe_status = CHAN_HOP_ERROR;
+        continue;    
+      }
+    }
+
+// *****************************************************************
+/// \> Cycle 300, If IOP and RFM cards, check own data diagnostics
+// *****************************************************************
+    // Deal with the own-data bits on the VMIC 5565 rfm cards
+    if (cdsPciModules.rfmCount > 0) {
+      if (cycleNum >= HKP_RFM_CHK_CYCLE && cycleNum < (HKP_RFM_CHK_CYCLE + cdsPciModules.rfmCount)) {
+        int mod = cycleNum - HKP_RFM_CHK_CYCLE;
+        status = vmic5565CheckOwnDataRcv(mod);
+        if(!status) ipcErrBits |= 4 + (mod * 4);
+      }
+      if (cycleNum >= (HKP_RFM_CHK_CYCLE + cdsPciModules.rfmCount) && cycleNum < (HKP_RFM_CHK_CYCLE + cdsPciModules.rfmCount*2)) {
+        int mod = cycleNum - HKP_RFM_CHK_CYCLE - cdsPciModules.rfmCount;
+        vmic5565ResetOwnDataLight(mod);
+      }
+      if (cycleNum >= (HKP_RFM_CHK_CYCLE + 2*cdsPciModules.rfmCount) && cycleNum < (HKP_RFM_CHK_CYCLE + cdsPciModules.rfmCount*3)) {
+        int mod = cycleNum - HKP_RFM_CHK_CYCLE - cdsPciModules.rfmCount*2;
+        // Write data out to the RFM to trigger the light
+        ((volatile long *)(cdsPciModules.pci_rfm[mod]))[2] = 0;
+      }
+    }
+
+// *****************************************************************
+/// \> Cycle 400 to 400 + numDacModules, write DAC heartbeat to AI chassis (only for 18 bit DAC modules)
+// DAC WD Write for 18 bit DAC modules
+// Check once per second on code cycle 400 to dac count
+// Only one write per code cycle to reduce time
+// *****************************************************************
+    if (cycleNum >= HKP_DAC_WD_CLK && cycleNum < (HKP_DAC_WD_CLK + cdsPciModules.dacCount)) 
+    {
+      if (cycleNum == HKP_DAC_WD_CLK) dacWatchDog ^= 1;
+      jj = cycleNum - HKP_DAC_WD_CLK;
+      if(cdsPciModules.dacType[jj] == GSC_18AO8)
+      {
+        volatile GSA_18BIT_DAC_REG *dac18bitPtr;
+        dac18bitPtr = (volatile GSA_18BIT_DAC_REG *)(dacPtr[jj]);
+        if(iopDacEnable && !dacChanErr[jj])
+          dac18bitPtr->digital_io_ports = (dacWatchDog | GSAO_18BIT_DIO_RW);
+      }
+      if(cdsPciModules.dacType[jj] == GSC_20AO8)
+      {
+        volatile GSA_20BIT_DAC_REG *dac20bitPtr;
+        dac20bitPtr = (volatile GSA_20BIT_DAC_REG *)(dacPtr[jj]);
+        if(iopDacEnable && !dacChanErr[jj]) 
+          dac20bitPtr->digital_io_ports = (dacWatchDog | GSAO_20BIT_DIO_RW);
+      }
+    }
+
+// *****************************************************************
+/// \> Cycle 500 to 500 + numDacModules, read back watchdog from AI chassis (18 bit DAC only)
+// AI Chassis WD CHECK for 18 bit DAC modules
+// Check once per second on code cycle HKP_DAC_WD_CHK to dac count
+// Only one read per code cycle to reduce time
+// *****************************************************************
+    if (cycleNum >= HKP_DAC_WD_CHK && cycleNum < (HKP_DAC_WD_CHK + cdsPciModules.dacCount)) 
+    {
+      jj = cycleNum - HKP_DAC_WD_CHK;
+      if(cdsPciModules.dacType[jj] == GSC_18AO8)
+      {
+        static int dacWDread = 0;
+        volatile GSA_18BIT_DAC_REG *dac18bitPtr = (volatile GSA_18BIT_DAC_REG *)(dacPtr[jj]);
+        dacWDread = dac18bitPtr->digital_io_ports;
+        if(((dacWDread >> 8) & 1) > 0)
+        {
+          pLocalEpics->epicsOutput.statDac[jj] &= ~(DAC_WD_BIT);
+        }
+        else 
+          pLocalEpics->epicsOutput.statDac[jj] |= DAC_WD_BIT;
+      }
+      if(cdsPciModules.dacType[jj] == GSC_20AO8)
+      {
+        static int dacWDread = 0;
+        volatile GSA_20BIT_DAC_REG *dac20bitPtr = (volatile GSA_20BIT_DAC_REG *)(dacPtr[jj]);
+        dacWDread = dac20bitPtr->digital_io_ports;
+        if(((dacWDread >> 8) & 1) > 0)
+          pLocalEpics->epicsOutput.statDac[jj] &= ~(DAC_WD_BIT);
+        else
+          pLocalEpics->epicsOutput.statDac[jj] |= DAC_WD_BIT;
+      }
+    }
+
+// *****************************************************************
+/// \> Cycle 600 to 600 + numDacModules, Check DAC FIFO Sizes to determine if DAC modules are synched to code 
+/// - ---- 18bit DAC reads out FIFO size dirrectly, but 16bit module only has a 4 bit register 
+/// area for FIFO empty, quarter full, etc. So, to make these bits useful in 16 bit module,
+/// code must set a proper FIFO size in map.c code.
+// This code runs once per second.
+// *****************************************************************
+#ifndef NO_DAC_PRELOAD
+   if (cycleNum >= HKP_DAC_FIFO_CHK && cycleNum < (HKP_DAC_FIFO_CHK + cdsPciModules.dacCount)) 
+   {
+      status = check_dac_buffers(cycleNum);
+	  printf("DAC status = %d\n",status);
+   }
+   if (dacTimingError) feStatus |= FE_ERROR_DAC;
+
+#endif
+
+// *****************************************************************
+// Update end of cycle information
+// *****************************************************************
+    // Capture end of cycle time.
+    rdtscl(cpuClock[CPU_TIME_CYCLE_END]);
+
+    /// \> Compute code cycle time diag information.
+    timeinfo.cycleTime = (cpuClock[CPU_TIME_CYCLE_END] - cpuClock[CPU_TIME_CYCLE_START])/CPURATE;
+    // Hold the max cycle time over the last 1 second
+    if(timeinfo.cycleTime > timeinfo.timeHold) { 
+      timeinfo.timeHold = timeinfo.cycleTime;
+      timeinfo.timeHoldWhen = cycleNum;
+    }
+    // Hold the max cycle time since last diag reset
+    if(timeinfo.cycleTime > timeinfo.timeHoldMax) timeinfo.timeHoldMax = timeinfo.cycleTime;
+    adcinfo.adcHoldTime = (cpuClock[CPU_TIME_CYCLE_START] - adcinfo.adcTime)/CPURATE;
+    // Avoid calculating the max hold time for the first few seconds
+    if (cycleNum != 0 && (timeinfo.startGpsTime+3) < cycle_gps_time) {
+      if(adcinfo.adcHoldTime > adcinfo.adcHoldTimeMax) 
+        adcinfo.adcHoldTimeMax = adcinfo.adcHoldTime;
+      if(adcinfo.adcHoldTime < adcinfo.adcHoldTimeMin) 
+        adcinfo.adcHoldTimeMin = adcinfo.adcHoldTime;
+      adcinfo.adcHoldTimeAvg += adcinfo.adcHoldTime;
+      if (adcinfo.adcHoldTimeMax > adcinfo.adcHoldTimeEverMax)  {
+        adcinfo.adcHoldTimeEverMax = adcinfo.adcHoldTimeMax;
+        adcinfo.adcHoldTimeEverMaxWhen = cycle_gps_time;
+      }
+      if (timeinfo.timeHoldMax > timeinfo.cpuTimeEverMax)  {
+        timeinfo.cpuTimeEverMax = timeinfo.timeHoldMax;
+        timeinfo.cpuTimeEverMaxWhen = cycle_gps_time;
+      }
+    }
+    adcinfo.adcTime = cpuClock[CPU_TIME_CYCLE_START];
+    // Calc the max time of one cycle of the user code
+    // For IOP, more interested in time to get thru ADC read code and send to slave apps
+    timeinfo.usrTime = (cpuClock[CPU_TIME_USR_START] - cpuClock[CPU_TIME_CYCLE_START])/CPURATE;
+    if(timeinfo.usrTime > timeinfo.usrHoldTime) timeinfo.usrHoldTime = timeinfo.usrTime;
+
+    /// \> Update internal cycle counters
+    cycleNum += 1;
+#ifdef DIAG_TEST
+    if(pLocalEpics->epicsInput.bumpCycle != 0) {
+      cycleNum += pLocalEpics->epicsInput.bumpCycle;
+      pLocalEpics->epicsInput.bumpCycle = 0;
+    }
+#endif
+    cycleNum %= CYCLE_PER_SECOND;
+    clock1Min += 1;
+    clock1Min %= CYCLE_PER_MINUTE;
+    if(subcycle == DAQ_CYCLE_CHANGE) 
+    {
+      daqCycle = (daqCycle + 1) % DAQ_NUM_DATA_BLOCKS_PER_SECOND;
+      if(!(daqCycle % 2)) pLocalEpics->epicsOutput.epicsSync = daqCycle;
+    }
+    if(subcycle == END_OF_DAQ_BLOCK) /*we have reached the 16Hz second barrier*/
+    {
+      /* Reset the data cycle counter */
+      subcycle = 0;
+    } else {
+      /* Increment the internal cycle counter */
+      subcycle ++;                                                
+    }
+
+/// \> If not exit request, then continue INFINITE LOOP  *******
+  }
+
+  // printf("exiting from fe_code()\n");
+  pLocalEpics->epicsOutput.cpuMeter = 0;
+
+
+  /* System reset command received */
+  return (void *)-1;
+}
-- 
GitLab