/*----------------------------------------------------------------------------*/
/**
* @mainpage
*
* Copyright (c) 2015 Southeastern Universities Research Association, *
* Thomas Jefferson National Accelerator Facility *
* *
* This software was developed under a United States Government license *
* described in the NOTICE file included as part of this distribution. *
* *
* Authors: Bryan Moffit *
* moffit@jlab.org Jefferson Lab, MS-12B3 *
* Phone: (757) 269-5660 12000 Jefferson Ave. *
* Fax: (757) 269-5800 Newport News, VA 23606 *
* *
*----------------------------------------------------------------------------*
*
* Description:
* Driver library for the Master Oscillator distribution module.
*
*----------------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include
#ifdef VXWORKS
#include
#include
#include
#include
#include
#else
#include
#include
#include "jvme.h"
#endif
#include "moLib.h"
#ifdef VXWORKS
IMPORT STATUS sysBusToLocalAdrs(int, char *, char **);
#endif
/* Mutex to guard MO read/writes */
pthread_mutex_t moMutex = PTHREAD_MUTEX_INITIALIZER;
#define MLOCK if(pthread_mutex_lock(&moMutex)<0) perror("pthread_mutex_lock");
#define MUNLOCK if(pthread_mutex_unlock(&moMutex)<0) perror("pthread_mutex_unlock");
/* Macro to check MOp */
#define CHECKMO { \
if(MOp == NULL) { \
logMsg("%s: ERROR : MO not initialized \n", \
(int)__FUNCTION__,2,3,4,5,6); \
return ERROR; \
} \
}
typedef struct mo_chan_struct
{
uint32_t div;
uint32_t dm;
} MO_CHAN;
/* Global Variables */
volatile struct mo_struct *MOp=NULL; /* pointer to MO memory map */
int moA24Offset=0; /* Difference in CPU A24 Base and VME A24 Base */
int moDefaultPS_0=16; /* Default value set for first level prescale */
MO_CHAN moChan[10] = /* Default divider and duty mode to set during initialization */
{
{ 6, 0 }, { 6, 0 }, { 6, 0 }, { 6, 0 }, { 6, 0 },
{ 6, 0 }, { 6, 0 }, { 6, 0 }, { 6, 0 }, { 6, 0 }
};
/**
* @defgroup Config Initialization/Configuration
* @defgroup Status Status
* @defgroup Deprec Deprecated - To be removed
*/
/**
* @ingroup Config
* @brief Initialize the MO register space into local memory,
* and perform initial default setup.
*
* @param tAddr VME A24 Address of MO
*
* @param iFlag Initialization bit mask
* - 0 Do not initialize the board, just setup the pointers to the registers
* - 1 Ignore firmware check
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moInit(uint32_t tAddr, uint32_t iFlag)
{
int skipFW=0, skipInit=0, stat=0, ichan=0;
uint32_t rval=0, fwvers=0;
unsigned long laddr=0;
if(iFlag & MO_INIT_NOINIT)
skipInit=1;
if(iFlag & MO_INIT_NOFWCHECK)
skipFW=1;
#ifdef VXWORKS
stat = sysBusToLocalAdrs(0x39,(char *)tAddr,(char **)&laddr);
if (stat != 0)
{
printf("flexioInit: ERROR: Error in sysBusToLocalAdrs res=%d \n",stat);
return ERROR;
}
#else
stat = vmeBusToLocalAdrs(0x39,(char *)tAddr,(char **)&laddr);
if (stat != 0)
{
printf("flexioInit: ERROR: Error in vmeBusToLocalAdrs res=%d \n",stat);
return ERROR;
}
#endif
moA24Offset = laddr - tAddr;
MOp = (struct mo_struct *)laddr;
/* Check if MO board is readable */
#ifdef VXWORKS
stat = vxMemProbe((char *)(&MOp->version),0,4,(char *)&rval);
#else
stat = vmeMemProbe((char *)(&MOp->version),4,(char *)&rval);
#endif
if (stat != OK)
{
printf("%s: ERROR: MO module not addressable\n",__FUNCTION__);
MOp=NULL;
return ERROR;;
}
if(((rval & MO_VERSION_ID_MASK)>>16) != MO_ID)
{
printf("%s: ERROR: Invalid Board ID: 0x%x (rval = 0x%08x)\n",
__FUNCTION__, rval & MO_VERSION_ID_MASK, rval);
MOp=NULL;
return ERROR;
}
printf("%s: MO VME (Local) address = 0x%08x (0x%lx)\n",
__FUNCTION__,
tAddr, laddr);
fwvers = rval & MO_VERSION_FWREV_MASK;
/* Check firmware version */
if(fwvers!=MO_SUPPORTED_VERSION)
{
if(skipFW)
{
printf("%s: WARN: Firmware Version (0x%x) not supported by this driver.\n\tSupported Version: 0x%x.\n",
__FUNCTION__, fwvers, MO_SUPPORTED_VERSION);
}
else
{
printf("%s: ERROR: Firmware Version (0x%x) not supported by this driver.\n\tSupported Version: 0x%x.\n",
__FUNCTION__, fwvers, MO_SUPPORTED_VERSION);
MOp=NULL;
return ERROR;
}
}
if(skipInit)
return OK;
/* Do the powerup type initialization here */
if(moReset()!=OK)
{
printf("%s: ERROR resetting MO.\n",
__FUNCTION__);
return ERROR;
}
if(moSetupClocks()!=OK)
{
printf("%s: ERROR setting up divider clocks.\n",
__FUNCTION__);
return ERROR;
}
/* Set up default prescale factors */
if(moConfigPS0(moDefaultPS_0)!=OK)
{
printf("%s: ERROR setting first level prescale\n",
__FUNCTION__);
return ERROR;
}
for(ichan=0;ichan<10;ichan++)
{
if(moConfigOutput(ichan, moChan[ichan].div, moChan[ichan].dm)!=OK)
{
printf("%s: ERROR setting channel %2d. divider = %2d, duty_mode = %d.\n",
__FUNCTION__,ichan, moChan[ichan].div, moChan[ichan].dm);
}
}
/* Sync dividers */
if(moSyncDividers()!=OK)
{
printf("%s: ERROR synchronizing dividers.\n",
__FUNCTION__);
return ERROR;
}
return OK;
}
/**
* @ingroup Config
* @brief Configures all output channels with common divider value 'divider' and
* duty cycle parameter 'duty_mode'
*
* @param divider Divider Value
*
* @param duty_mode Duty cycle mode
* - 0: duty cycle closest to 50% achievable for given divider
* - 1: minimum duty cycle achievable for given divider
* - 2: maximum duty cycle achievable for given divider
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moConfigCommon(int divider, int duty_mode)
{
int ichan;
CHECKMO;
if( (divider < 1) || (divider > 32) )
{
printf("%s: Invalid divider value (%d)\n",
__FUNCTION__,divider);
return ERROR;
}
if( (duty_mode < 0) || (duty_mode > 2) )
{
printf("%s: Invalid duty_mode value (%d)\n",
__FUNCTION__,duty_mode);
return ERROR;
}
if(moReset()!=OK)
{
printf("%s: ERROR resetting MO.\n",
__FUNCTION__);
return ERROR;
}
if(moSetupClocks()!=OK)
{
printf("%s: ERROR setting up divider clocks.\n",
__FUNCTION__);
return ERROR;
}
for(ichan=0;ichan<10;ichan++)
{
if(moConfigOutput(ichan, divider, duty_mode)!=OK)
{
printf("%s: ERROR setting channel %2d. divider = %2d, duty_mode = %d.\n",
__FUNCTION__,ichan, divider, duty_mode);
}
}
if(moSyncDividers()!=OK)
{
printf("%s: ERROR synchronizing dividers.\n",
__FUNCTION__);
return ERROR;
}
return OK;
}
/**
* @ingroup Config
* @brief Synchronize all dividers
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moSyncDividers()
{
uint32_t divsel = 0;
CHECKMO;
divsel = MO_DIV_CTRL_DIV0_SELECT | MO_DIV_CTRL_DIV1_SELECT;
MLOCK;
vmeWrite32(&MOp->div_ctrl, divsel | 0x5804); // sync all dividers (must toggle bit)
vmeWrite32(&MOp->div_ctrl, divsel | 0x5A01); // update register
vmeWrite32(&MOp->div_ctrl, divsel | 0x5800);
vmeWrite32(&MOp->div_ctrl, divsel | 0x5A01); // update register
MUNLOCK;
return OK;
}
/**
* @ingroup Config
*
* @brief Reset Module to power-up state.
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moReset()
{
uint32_t divsel=0;
CHECKMO;
MLOCK;
vmeWrite32(&MOp->csr, MO_CSR_HARD_RESET); // hard reset module
divsel = MO_DIV_CTRL_DIV0_SELECT | MO_DIV_CTRL_DIV1_SELECT;
vmeWrite32(&MOp->div_ctrl, divsel | 0x0030); // soft reset both divider chips (must toggle bit)
vmeWrite32(&MOp->div_ctrl, divsel | 0x0010); // (DON'T need to update register bits for addr=00!)
MUNLOCK;
return OK;
}
/**
* @ingroup Config
*
* @brief Reset divider chips to power-up state
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moResetDividers()
{
uint32_t divsel=0;
CHECKMO;
MLOCK;
divsel = MO_DIV_CTRL_DIV0_SELECT | MO_DIV_CTRL_DIV1_SELECT;
vmeWrite32(&MOp->div_ctrl, divsel | 0x0030); // soft reset both divider chips (must toggle bit)
vmeWrite32(&MOp->div_ctrl, divsel | 0x0010); // (DON'T need to update register bits for addr=00!)
MUNLOCK;
return OK;
}
/**
* @ingroup Config
*
* @brief Setup standard clock configuration
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moSetupClocks()
{
uint32_t value_0, value_1;
CHECKMO;
MLOCK;
vmeWrite32(&MOp->div_ctrl, MO_DIV_CTRL_DIV0_SELECT | 0x4502); // use CLK2 input for DIV 0 divider, power down CLK1
vmeWrite32(&MOp->div_ctrl, MO_DIV_CTRL_DIV1_SELECT | 0x4505); // use CLK1 input for DIV 1 divider, power down CLK2
vmeWrite32(&MOp->div_ctrl, MO_DIV_CTRL_DIV0_SELECT | MO_DIV_CTRL_DIV1_SELECT |
MO_DIV_CTRL_READ_SER_DATA | 0x4500); // read both divider chips
vmeWrite32(&MOp->div_ctrl, MO_DIV_CTRL_DIV0_SELECT | MO_DIV_CTRL_DIV1_SELECT | 0x5A01); // update registers
value_0 = vmeRead32(&MOp->div_read[0]);
value_1 = vmeRead32(&MOp->div_read[1]);
MUNLOCK;
// printf("div addr = %X reg 0 = %X reg 1 = %X\n", 0x45, value_0, value_1);
return OK;
}
/**
* @ingroup Config
*
* @brief Configures 'output' (0-9) with divider value 'divider' (1-32)
* and duty cycle mode 'duty_mode' (0-2)
*
* @param output Output channel
* @param divider Divider value
* @param duty_mode Duty cycle mode
* - 0: duty cycle closest to 50% achievable for given divider
* - 1: minimum duty cycle achievable for given divider
* - 2: maximum duty cycle achievable for given divider
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moConfigOutput(int output, int divider, int duty_mode)
{
uint32_t addr[10] = { 0x4E, 0x4C, 0x52, 0x50, 0x4A, // chip address map for outputs (0-9)
0x4E, 0x4C, 0x52, 0x50, 0x4A };
uint32_t offset, high, low, div_value;
CHECKMO;
if( (output < 0) || (output > 9)) // check if inputs are within bounds
{
printf("%s: Invalid output value (%d)\n",
__FUNCTION__,output);
return ERROR;
}
if( (divider < 1) || (divider > 32))
{
printf("%s: Invalid divider value (%d)\n",
__FUNCTION__,divider);
return ERROR;
}
if( (duty_mode < 0) || (duty_mode > 2))
{
printf("%s: Invalid duty_mode value (%d)\n",
__FUNCTION__,duty_mode);
return ERROR;
}
if( output <= 4 )
offset = MO_DIV_CTRL_DIV0_SELECT; // divider 0 chip
else
offset = MO_DIV_CTRL_DIV1_SELECT; // divider 1 chip
MLOCK;
if( divider == 1 ) // must bypass divider (write 0x80 to next address)
{
vmeWrite32(&MOp->div_ctrl, offset | ((addr[output] + 1) << 8) | 0x80);
}
else // for all other divider values
{
switch( duty_mode )
{
case 0: // 50% (or closest achievable) duty cycle
high = (int)(divider/2);
low = divider - high;
break;
case 1:
if( divider < 18 ) // minimum duty cycle achievable
high = 1;
else
high = divider - 16;
low = divider - high;
break;
case 2: // maximum duty cycle achievable
if( divider < 18 )
low = 1;
else
low = divider - 16;
high = divider - low;
break;
}
div_value = ((low - 1) << 4) | (high - 1);
// printf("output = %d LO = %X HI = %X div_value = %X\n", output, (low - 1), (high - 1), div_value);
vmeWrite32(&MOp->div_ctrl, offset | ((addr[output] + 1) << 8) | 0x0); // clear bypass divider
vmeWrite32(&MOp->div_ctrl, offset | (addr[output] << 8) | div_value);
}
vmeWrite32(&MOp->div_ctrl, offset | 0x5A01); // update registers
MUNLOCK;
return OK;
}
/**
* @ingroup Status
* @brief Print configuration of all dividers
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moConfigPrint()
{
uint32_t ps0[4] = { 2, 4, 8, 16 };
uint32_t addr[10] = { 0x4E, 0x4C, 0x52, 0x50, 0x4A, // chip address map for outputs (0-9)
0x4E, 0x4C, 0x52, 0x50, 0x4A };
uint32_t ps0_value, output, divider, high, low, chip, offset, reg_value;
uint32_t total_divide;
int ii;
float duty_cycle;
CHECKMO;
MLOCK;
ps0_value = 0x3 & vmeRead32(&MOp->ps0_ctrl);
printf("\n\n%s:\ninitial prescale factor = %d\n",
__FUNCTION__, ps0[ps0_value]);
printf("\noutput divider total divide duty cycle\n");
printf("------------------------------------------\n");
for(ii=0;ii<10;ii++)
{
output = ii;
if( (output >= 0) && (output <= 4) )
{
offset = MO_DIV_CTRL_DIV0_SELECT; // divider 0 chip
chip = 0;
}
else
{
offset = MO_DIV_CTRL_DIV1_SELECT; // divider 1 chip
chip = 1;
}
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
((addr[output] + 1) << 8)); // check if divider is bypassed
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
if( reg_value & 0x80 )
{
divider = 1;
duty_cycle = 0.50;
}
else
{
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
(addr[output] << 8)); // find divider, duty cycle
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
high = (0xF & reg_value) + 1;
low = (0xF & (reg_value >> 4)) + 1;
divider = high + low;
duty_cycle = ((float)(high))/((float)(divider));
}
total_divide = ps0[ps0_value] * divider;
printf("%4d %4d %6d %6.3f \n",
output, divider, total_divide, duty_cycle);
}
printf("------------------------------------------\n\n");
MUNLOCK;
return OK;
}
/**
* @ingroup Config
* @brief Set up Initial Prescale factor (2,4,8,16)
*
* @param ps0 Prescale factor
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moConfigPS0(int ps0)
{
uint32_t value;
CHECKMO;
switch(ps0)
{
case 2:
value = 0;
break;
case 4:
value = 1;
break;
case 8:
value = 2;
break;
case 16:
value = 3;
break;
default:
printf("%s: ERROR: Invalid ps0 value (%d). Setting to 2.\n",
__FUNCTION__,ps0);
value = 0;
}
MLOCK;
vmeWrite32(&MOp->ps0_ctrl, value);
MUNLOCK;
return OK;
}
/**
* @ingroup Config
* @brief Set second stage prescale factor for selected channel
*
* @param channel Selected Channel
* @param prescale Second stage prescale factor
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moSetPrescale(uint32_t channel, uint32_t prescale)
{
uint32_t duty_mode=0;
CHECKMO;
if((channel<0) || (channel>9))
{
printf("%s: ERROR: Invalid channel (%d)\n",
__FUNCTION__,channel);
return ERROR;
}
if((prescale<1) || (prescale>32))
{
printf("%s: ERROR: Invalid prescale (%d)\n",
__FUNCTION__,prescale);
return ERROR;
}
if(moGetDutyMode(channel, &duty_mode)!=OK)
{
printf("%s: ERROR: Failled to get current duty_mode for channel %d\n",
__FUNCTION__,channel);
return ERROR;
}
if(moConfigOutput(channel, prescale, duty_mode)!=OK)
{
printf("%s(%d,%d): ERROR: Unable to set prescale.\n",
__FUNCTION__,channel, prescale);
return ERROR;
}
if(moSyncDividers()!=OK)
{
printf("%s: ERROR synchronizing dividers.\n",
__FUNCTION__);
return ERROR;
}
return OK;
}
/**
* @ingroup Status
* @brief Get second stage prescale factor for selected channel
*
* @param channel Selected Channel
* @param *prescale Address to store second stage prescale factor
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moGetPrescale(uint32_t channel, uint32_t *prescale)
{
uint32_t addr[10] = { 0x4E, 0x4C, 0x52, 0x50, 0x4A, // chip address map for outputs (0-9)
0x4E, 0x4C, 0x52, 0x50, 0x4A };
uint32_t div_bypass=0, div=0, divsel=0;
int chip=0;
CHECKMO;
if((channel>=0) && (channel<=4))
{
divsel = MO_DIV_CTRL_DIV0_SELECT;
chip=0;
}
else if((channel>=5) && (channel<=9))
{
divsel = MO_DIV_CTRL_DIV1_SELECT;
chip=1;
}
else
{
printf("%s: ERROR: Invalid channel (%d)\n",
__FUNCTION__,channel);
return ERROR;
}
if(!prescale)
{
printf("%s: ERROR: Invalid prescale pointer\n",
__FUNCTION__);
return ERROR;
}
MLOCK;
vmeWrite32(&MOp->div_ctrl, divsel | MO_DIV_CTRL_READ_SER_DATA | (addr[channel]+1)<<8);
div_bypass = vmeRead32(&MOp->div_read[chip]) & MO_DIV_READ_DATA_MASK;
/* Serial data math */
if(div_bypass & 0x80)
div = 1;
else
{
vmeWrite32(&MOp->div_ctrl, divsel | MO_DIV_CTRL_READ_SER_DATA | (addr[channel])<<8);
div = vmeRead32(&MOp->div_read[chip]) & MO_DIV_READ_DATA_MASK;
div = ((div&0xF)+1) + (((div&0xF0)>>4)+1);
}
*prescale = div;
MUNLOCK;
return OK;
}
/**
* @ingroup Config
* @brief Set second stage duty_mode for selected channel
*
* @param channel Selected Channel
* @param duty_mode Second stage duty_mode
* - 0: duty cycle closest to 50% achievable for given divider
* - 1: minimum duty cycle achievable for given divider
* - 2: maximum duty cycle achievable for given divider
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moSetDutyMode(uint32_t channel, int duty_mode)
{
uint32_t prescale=0;
CHECKMO;
if((channel<0) || (channel>9))
{
printf("%s: ERROR: Invalid channel (%d)\n",
__FUNCTION__,channel);
return ERROR;
}
if((duty_mode<0) || (duty_mode>2))
{
printf("%s: ERROR: Invalid duty_mode (%d)\n",
__FUNCTION__,duty_mode);
return ERROR;
}
if(moGetPrescale(channel,&prescale)!=OK)
{
printf("%s: ERROR: Failed to obtain current prescale for channel %d\n",
__FUNCTION__,channel);
return ERROR;
}
if(moConfigOutput(channel, prescale, duty_mode)!=OK)
{
printf("%s(%d,%d): ERROR: Unable to set prescale.\n",
__FUNCTION__,channel, prescale);
return ERROR;
}
if(moSyncDividers()!=OK)
{
printf("%s: ERROR synchronizing dividers.\n",
__FUNCTION__);
return ERROR;
}
return OK;
}
/**
* @ingroup Status
* @brief Get second stage duty_mode for selected channel
*
* @param channel Selected Channel
* @param *duty_mode Address to store Second stage duty_mode
* - 0: duty cycle closest to 50% achievable for given divider
* - 1: minimum duty cycle achievable for given divider
* - 2: maximum duty cycle achievable for given divider
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moGetDutyMode(uint32_t channel, uint32_t *duty_mode)
{
uint32_t addr[10] = { 0x4E, 0x4C, 0x52, 0x50, 0x4A, // chip address map for outputs (0-9)
0x4E, 0x4C, 0x52, 0x50, 0x4A };
uint32_t output, divider, high, low, chip, offset, reg_value;
CHECKMO;
if((channel<0) || (channel>9))
{
printf("%s: ERROR: Invalid channel (%d)\n",
__FUNCTION__,channel);
return ERROR;
}
if(!duty_mode)
{
printf("%s: ERROR: Invalid duty_mode pointer\n",
__FUNCTION__);
return ERROR;
}
if( (channel >= 0) && (channel <= 4) )
{
offset = MO_DIV_CTRL_DIV0_SELECT; // divider 0 chip
chip = 0;
}
else
{
offset = MO_DIV_CTRL_DIV1_SELECT; // divider 1 chip
chip = 1;
}
MLOCK;
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
((addr[channel] + 1) << 8)); // check if divider is bypassed
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
if( reg_value & 0x80 )
{
*duty_mode = 0;
}
else
{
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
(addr[channel] << 8)); // find divider, duty cycle
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
high = (0xF & reg_value) + 1;
low = (0xF & (reg_value >> 4)) + 1;
divider = high + low;
if(high == ((int)(divider/2)))
{
*duty_mode = 0;
}
else
{
if(divider<18)
{
if(high==1)
*duty_mode = 1;
else
*duty_mode = 2;
}
else
{
if(high == (divider-16))
*duty_mode = 1;
else
*duty_mode = 2;
}
}
}
MUNLOCK;
return OK;
}
/**
* @ingroup Status
* @brief Get second stage duty cycle for selected channel
*
* @param channel Selected Channel
* @param *duty_cycle Address to store Second stage duty cycle (in percent)
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moGetDutyCycle(uint32_t channel, float *duty_cycle)
{
uint32_t addr[10] = { 0x4E, 0x4C, 0x52, 0x50, 0x4A, // chip address map for outputs (0-9)
0x4E, 0x4C, 0x52, 0x50, 0x4A };
uint32_t output, divider, high, low, chip, offset, reg_value;
CHECKMO;
if((channel<0) || (channel>9))
{
printf("%s: ERROR: Invalid channel (%d)\n",
__FUNCTION__,channel);
return ERROR;
}
if(!duty_cycle)
{
printf("%s: ERROR: Invalid duty_cycle pointer\n",
__FUNCTION__);
return ERROR;
}
if( (channel >= 0) && (channel <= 4) )
{
offset = MO_DIV_CTRL_DIV0_SELECT; // divider 0 chip
chip = 0;
}
else
{
offset = MO_DIV_CTRL_DIV1_SELECT; // divider 1 chip
chip = 1;
}
MLOCK;
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
((addr[channel] + 1) << 8)); // check if divider is bypassed
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
if( reg_value & 0x80 )
{
*duty_cycle = 0.500;
}
else
{
vmeWrite32(&MOp->div_ctrl, offset | MO_DIV_CTRL_READ_SER_DATA |
(addr[channel] << 8)); // find divider, duty cycle
reg_value = 0xFF & vmeRead32(&MOp->div_read[chip]);
high = (0xF & reg_value) + 1;
low = (0xF & (reg_value >> 4)) + 1;
divider = high + low;
*duty_cycle = ((float)(high))/((float)(divider));
}
MUNLOCK;
return OK;
}
int moSetInitialPrescale( uint32_t prescale ) {
return moConfigPS0( prescale );
}
int moGetInitialPrescale( uint32_t *prescale ) {
int err = OK;
uint32_t ps0[4] = { 2, 4, 8, 16 };
CHECKMO;
MLOCK;
uint32_t ps0_value = 0x3 & vmeRead32( &MOp->ps0_ctrl );
if ( 0 > ps0_value || ps0_value > 3 ) {
*prescale = 0;
err = ERROR;
} else {
*prescale = ps0[ps0_value];
}
MUNLOCK;
return err;
}
/**
* @ingroup Status
* @brief Test data integrity by writing and reading test register
*
* @return OK if successful, otherwise ERROR.
*
*/
int
moTestAccess()
{
uint32_t data_value=0, read_value=0;
int error=OK, ii;
CHECKMO;
printf("\n\n%s: Begin data integrity test\n",__FUNCTION__);
MLOCK;
data_value = 0x0;
vmeWrite32(&MOp->test, data_value);
read_value = vmeRead32(&MOp->test);
if( read_value != data_value )
{
error = ERROR;
printf("***** data error: data write = %X data read = %X\n", data_value, read_value);
}
data_value = 0xFFFFFFFF;
vmeWrite32(&MOp->test, data_value);
read_value = vmeRead32(&MOp->test);
if( read_value != data_value )
{
error = ERROR;
printf("***** data error: data write = %X data read = %X\n", data_value, read_value);
}
data_value = 0xAAAAAAAA;
vmeWrite32(&MOp->test, data_value);
read_value = vmeRead32(&MOp->test);
if( read_value != data_value )
{
error = ERROR;
printf("***** data error: data write = %X data read = %X\n", data_value, read_value);
}
data_value = 0x55555555;
vmeWrite32(&MOp->test, data_value);
read_value = vmeRead32(&MOp->test);
if( read_value != data_value )
{
error = ERROR;
printf("***** data error: data write = %X data read = %X\n", data_value, read_value);
}
data_value = 1;
for(ii=0;ii<32;ii++)
{
vmeWrite32(&MOp->test, data_value);
read_value = vmeRead32(&MOp->test);
if( read_value != data_value )
{
error = ERROR;
printf("***** data error: data write = %X data read = %X\n", data_value, read_value);
}
data_value = data_value * 2;
}
if( error == 0 )
printf("\n----- data transfers O.K. -----\n\n");
MUNLOCK;
return error;
}