Files
pcas/src/dev/devVxiTDM.c
1995-11-29 14:38:52 +00:00

656 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* devVxiTDM.c */
/* base/src/dev $Id$ */
/* Device Support Routines for Custom Vxi Time Delay Module */
/*
* Author: Jim Kowalkowski
* Date: 8/20/92
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 08-20-92 jbk Initial Implementation
* .02 09-18-92 jbk frame/slot addressing added
* .03 08-31-93 joh updated for changed epvxi
* status format
* ...
*/
/* Since this is a custom module, there is no user manual. The
information supplied here summorizing the usage.
Simplified diagram of board:
input 1 ---->|
| (delay module section)
input 2 ---->|
|---->4:1Mux---->8-bit digital delay line--->gate--+--->
input 3 ---->| 0-1.275us in 5ns steps |
| V
input 4 ---->| last
value
Ten delay module sections exist for the four inputs. Each one has an
associated vxi register. So altogether there are ten of the
following registers.
_________________________________________________________________
bits |15 |14 |13 |12 |11 |10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------+---+---+-------+-------------------------------|
write | unused | |TE | TS | delay setting in ns |
|---------------+---+---+-------+-------------------------------|
read | unused |TD |TE | TS | delay setting in ns |
+---------------+---+---+-------+-------------------------------+
where TE = trigger enable/disable
TS = trigger select (input 1-4)
TD = trigger detect (trigger detected/not detected)
*/
#include <vxWorks.h>
#include <vme.h>
#include <types.h>
#include <ctype.h>
#include <stdioLib.h>
#include <string.h>
#include <iv.h>
#include <drvEpvxi.h>
#include <alarm.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <recSup.h>
#include <devSup.h>
#include <dbScan.h>
#include <special.h>
#include <module_types.h>
#include <eventRecord.h>
#include <pulseDelayRecord.h>
/* Create the dsets for devVxiTDM */
static long report();
static long init();
static long init_pd();
static long get_ioint_info();
static long write_pd();
static long get_enum();
typedef struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
DEVSUPFUN get_enum;
} TDM_DSET;
/*It doesnt matter who honors report or init thus let 1st devSup do it*/
TDM_DSET devPdVxiTDM = { 6, report, init, init_pd, NULL, write_pd, get_enum };
volatile int VxiTDMDebug=0;
#define Debug(f,v) { if(VxiTDMDebug) printf(f,v); }
/* definitions related to fields of records*/
/* defs for gtyp and ctyp fields */
#define ACTIVE 1
#define INACTIVE 0
/* Trigger Source Selection - all macros require short for reg */
#define TRGS_CLEAR(reg) (reg&0xfcff)
#define TRGS_SET(trgs,reg) (TRGS_CLEAR(reg)|trgs<<8)
#define TRGS_VALUE(reg) ((reg&0x0300)>>8)
#define TRGS1 0x0000
#define TRGS2 0x0100
#define TRGS3 0x0200
#define TRGS4 0x0300
#define TRIG_ENABLE(reg) (reg|0x0400)
#define TRIG_DISABLE(reg) (reg&(~0x400))
#define TRIG_VALUE(reg) ((reg&0x0400)?1:0)
#define DELAY_CLEAR(reg) (reg&0xff00)
#define DELAY_SET(delay,reg) (DELAY_CLEAR(reg)|delay)
#define DELAY_VALUE(reg) (reg&0x00ff)
#define TRIG_DETECT(reg) (0x0800&reg)
#define REG_OFFSET 0x0020
/* EPICS_VXI_LA_COUNT is a global variable that must be present in the IOC */
#define MAXCARDS EPICS_VXI_LA_COUNT
#define MAXSIG 9
#define MAX_NANOSEC 1275e1
#define MAX_DELAY 255e1
#define TIME_FACT MAX_DELAY/MAX_NANOSEC
#define TIME_FACT_TO MAX_NANOSEC/MAX_DELAY
#define VXI_MAKE_TDM 0xee4 /* official is 3812 */
#define VXI_MODEL_TDM 0xf01 /* test one 0xf01 */
unsigned long VxiTDM_make = VXI_MAKE_TDM;
unsigned long VxiTDM_model = VXI_MODEL_TDM;
static unsigned long tdmDriverID;
/* config area for each card */
/* for stat-> bit per channel 1=used/valid, 0=unused/invalid, bit-0=channel-0 */
struct tdm_config {
FAST_LOCK lock;
unsigned short stat;
};
/* time unit conversion constants - want in nanoseconds
seconds,milliseconds,microseconds,nanoseconds,picoseconds */
static double cons[] = {
1e9,1e6,1e3,1,1e-3 };
static void tdmInitLA();
static void tdm_stat();
static void tdm_report();
/***************************************************************/
/*
report for device - just uses the vxi report
*/
static long report(int interest)
{
unsigned short la,signal;
struct vxi_csr *pcsr;
struct tdm_config *tc;
epvxiDeviceSearchPattern dsp;
printf("Report for Vxi Time Delay Module\n");
dsp.flags=VXI_DSP_make|VXI_DSP_model;
dsp.make=VxiTDM_make; /* VXI_MAKE_TDM; */
dsp.model=VxiTDM_model; /* VXI_MODEL_TDM; */
if( epvxiLookupLA(&dsp,tdm_report,(void *)&interest)!=VXI_SUCCESS)
return ERROR;
return OK;
}
static void tdm_report(unsigned la,void *interest)
{
tdm_stat(la,(int)*(int *)interest);
return;
}
/***************************************************************/
/*
Initialize all the TDM cards
*/
static long init(int after)
{
epvxiDeviceSearchPattern dsp;
if(after) return(0);
tdmDriverID=epvxiUniqueDriverID();
dsp.flags=VXI_DSP_make|VXI_DSP_model;
dsp.make=VxiTDM_make; /* VXI_MAKE_TDM; */
dsp.model=VxiTDM_model; /* VXI_MODEL_TDM; */
if( epvxiLookupLA(&dsp,tdmInitLA,(void *)NULL)!=VXI_SUCCESS)
{
logMsg("No ANL Time Delay Modules found\n");
return ERROR;
}
if(epvxiRegisterMakeName(VxiTDM_make,"ANL")!=VXI_SUCCESS) /* VXI_MAKE_TDM */
logMsg("%s: unable to register MAKE\n",__FILE__);
if(epvxiRegisterModelName(VxiTDM_make,VxiTDM_model,
"Time Delay Module")!=VXI_SUCCESS) /* VXI_MAKE_TDM VXI_MODEL_TDM */
logMsg("%s: unable to register MODEL\n",__FILE__);
return OK;
}
/***************************************************************/
/*
Initialize one TDM card
*/
static void tdmInitLA(unsigned la)
{
int status;
struct vxi_csr *pcsr;
struct tdm_config *tc;
Debug("tdmInitLA() la=%d \n",la);
status=epvxiOpen(la,tdmDriverID,sizeof(struct tdm_config),tdm_stat);
if(status!=VXI_SUCCESS)
{
logMsg("%s:Device Open Failed (stat=%d)(LA=0x%02X)\n",
__FILE__,status,la);
return;
}
pcsr=VXIBASE(la);
tc=epvxiPConfig(la,tdmDriverID,struct tdm_config *);
if(!tc)
{
epvxiClose(la,tdmDriverID);
return;
}
FASTLOCKINIT(&tc->lock);
tc->stat=0;
return;
}
/***************************************************************/
/*
Initialize a pulse delay record channel (signal)
*/
static long init_pd(struct pulseDelayRecord *pd)
{
struct vxiio *pvxiio = (struct vxiio *)&(pd->out.value);
unsigned short dummy;
short la,signal;
unsigned short *channel_reg;
static unsigned epvxiGetLa();
struct vxi_csr *pcsr;
struct tdm_config *tc;
long status;
/* out must be an VXI_IO */
switch (pd->out.type)
{
case (VXI_IO):
break;
default :
recGblRecordError(S_dev_badBus,(void *)pd,
"devVxiTDM (init_record) Illegal OUT Bus Type");
return(S_dev_badBus);
}
/* initialize to null indicating no address found */
if((la=epvxiGetLa(pvxiio))<0)
{
recGblRecordError(S_dev_badSignal,(void *)pd,
"devVxiTDM (init_record) Illegal frame/slot");
return(S_dev_badSignal);
}
Debug("got la = %d\n",la);
if(isdigit(pvxiio->parm[0]))
signal=pvxiio->parm[0]-'0';
else
{
recGblRecordError(S_dev_badSignal,(void *)pd,
"devVxiTDM (init_record) Illegal SIGNAL field");
return(S_dev_badSignal);
}
Debug("frame = %d \n",pvxiio->frame);
Debug("slot = %d \n",pvxiio->slot);
Debug("la = %d \n",pvxiio->la);
Debug("flag = %d \n",pvxiio->flag);
Debug("signal = %d \n",signal);
Debug("parm = (%s) \n",pvxiio->parm);
if(la>=MAXCARDS)
{
recGblRecordError(S_dev_badCard,(void *)pd,
"devVxiTDM (init_record) exceeded maximum supported cards");
return(S_dev_badCard);
}
if(signal>MAXSIG)
{
recGblRecordError(S_dev_badSignal,(void *)pd,
"devVxiTDM (init_record) Illegal SIGNAL field");
return(S_dev_badSignal);
}
/* get the address of the register for this la and signal */
status = epvxiFetchPConfig(la,tdmDriverID,tc);
if(status)
{
recGblRecordError(status,(void *)pd,
"devVxiTDM (init_record) No card at address");
return(status);
}
pcsr=VXIBASE(la);
(char *)channel_reg=(char *)pcsr+REG_OFFSET;
Debug("channel_reg = %08.8X \n",channel_reg);
Debug("tc->stat = %x \n",tc->stat);
if( tc->stat&(1<<signal) )
{
recGblRecordError(S_dev_badSignal,(void *)pd,
"devVxiTDM (init_record) signal/la already in use");
return(S_dev_badSignal);
}
FASTLOCK(&tc->lock);
/* check if register readable */
if(vxMemProbe((char*)(&channel_reg[signal]),READ,
sizeof(unsigned short),(char*)&dummy)!=OK)
{
recGblRecordError(S_dev_badSignal,(void *)pd,
"devVxiTDM (init_record) vxMemProbe failed for signal/la");
return(S_dev_badSignal);
}
/* enable/disable gate */
switch(pd->gate)
{
default:
case ACTIVE:
channel_reg[signal]=TRIG_ENABLE(channel_reg[signal]);
break;
case INACTIVE:
channel_reg[signal]=TRIG_DISABLE(channel_reg[signal]);
break;
}
FASTUNLOCK(&tc->lock);
tc->stat|=(1<<signal);
/*
This field is used to indicate the address of the field
which caused processing of the record which is none now.
*/
pd->dpvt=(void *)pd;
return(0);
}
/***************************************************************/
/*
Set/read the values for a channel
*/
static long write_pd(struct pulseDelayRecord *pr)
{
unsigned short *channel_reg;
unsigned short la,signal;
unsigned short delay_time,channel_value,channel_read;
struct vxiio *pvxiio;
struct vxi_csr *pcsr;
struct tdm_config *tc;
long status;
Debug("Entering write_pd for pulseDelay\n",0);
/* will be null if init failed */
if(!pr->dpvt) return(S_dev_NoInit);
pvxiio = (struct vxiio *)&(pr->out.value);
la = pvxiio->la;
if(isdigit(pvxiio->parm[0]))
signal=pvxiio->parm[0]-'0';
else
{
recGblRecordError(S_dev_badSignal,(void *)pr,
"devVxiTDM (init_record) Illegal SIGNAL field");
return(S_dev_badSignal);
}
/* get address of register for this card and signal */
status=epvxiFetchPConfig(la,tdmDriverID,tc);
if(status){
return status;
}
pcsr=VXIBASE(la);
(char *)channel_reg=(char *)pcsr+REG_OFFSET;
Debug("Channel base address = 0x%08.8X \n",channel_reg);
Debug("Channel register address = 0x%08.8X \n",&channel_reg[signal]);
FASTLOCK(&tc->lock);
/* set val to active if trigger detect on and a periodic scan
caused processing, special processing will set pfld */
/* only lower 2 bytes used in pfld - kludge to be removed later */
if(!(pr->pfld&0x00ff))
{
channel_read=channel_reg[signal];
if(TRIG_DETECT(channel_read))
pr->val=ACTIVE;
else
pr->val=INACTIVE;
pr->udf=FALSE;
pr->dly=(double)DELAY_VALUE(channel_read)*TIME_FACT_TO;
pr->hts=TRGS_VALUE(channel_read);
pr->gate=TRIG_VALUE(channel_read);
Debug("trigger detect read - value = %d\n",pr->val);
}
else
{
/* initialize new channel value */
channel_value=0x0000;
/* compute delay time */
delay_time=(unsigned short)(cons[pr->unit]*pr->dly*TIME_FACT);
Debug("delay time = %d\n",delay_time);
Debug("units = %d \n",pr->unit);
if(delay_time > 255)
{
recGblSetSevr(pr,WRITE_ALARM,INVALID_ALARM);
recGblRecordError(S_db_badField,(void *)pr,
"invalid delay value - set to max");
delay_time=255;
}
channel_value=DELAY_SET(delay_time,channel_value);
/* enable/disable gate */
switch(pr->gate)
{
default:
case ACTIVE:
channel_value=TRIG_ENABLE(channel_value);
break;
case INACTIVE:
channel_value=TRIG_DISABLE(channel_value);
break;
}
/* trigger selection set */
if(pr->hts > 3)
{
recGblSetSevr(pr,WRITE_ALARM,INVALID_ALARM);
recGblRecordError(S_db_badField,(void *)pr,
"invalid trigger value");
return(0);
}
channel_value=TRGS_SET(pr->hts,channel_value);
/* write to register */
channel_reg[signal]=channel_value;
Debug("Ending channel value = %04.4X\n",channel_value);
}
FASTUNLOCK(&tc->lock);
return(0);
}
/***************************************************************/
/*
report for vxi routines
*/
static void tdm_stat(unsigned la,int level)
{
struct tdm_config *tc;
struct vxi_csr *pcsr;
int i;
long status;
status = epvxiFetchPConfig(la,tdmDriverID,tc);
if(status){
errMessage(status,NULL);
return;
}
pcsr=VXIBASE(la);
Debug("Report level = %d\n",level);
printf("Report for Vxi Time Delay module %d:\n",la);
printf(" Signals in use:");
for(i=0;i<=MAXSIG;i++) if(tc->stat&(1<<i)) printf("%d ",i);
printf("\n");
}
/***************************************************************/
/*
Routines to fill vxiio stucture -
always returns la
calculates la from frame/slot or figures out frame/slot
from la
*/
#define MAX_VXI_SLOTS 12
#define MAX_VXI_FRAMES 255
static void epvxiGetLA_one(unsigned la,void *pvxiio)
{
struct vxiio *pvxi=(struct vxiio *)pvxiio;
Debug("epvxiGetLA_one() la = %x \n",la);
pvxi->la=(short)la;
return;
}
static unsigned epvxiGetLa(struct vxiio *pvxiio,unsigned long vxiDriverID)
{
epvxiDeviceSearchPattern dsp;
long status;
if(pvxiio->flag==0) /* frame */
{
dsp.flags=VXI_DSP_slot|VXI_DSP_slot_zero_la;
if(pvxiio->slot > MAX_VXI_SLOTS || pvxiio->slot < 0 ||
pvxiio->frame > MAX_VXI_FRAMES || pvxiio->frame < 0)
return ERROR;
dsp.slot=(unsigned char)pvxiio->slot;
dsp.slot_zero_la=(unsigned char)pvxiio->frame;
Debug("epvxiGetLa() dsp.slot=%d\n",dsp.slot);
Debug("epvxiGetLa() dsp.slotzero_la=%d\n",dsp.slot_zero_la);
status = epvxiLookupLA(&dsp,epvxiGetLA_one,(void *)pvxiio);
if(status)
return ERROR;
}
else /* SA - static address (la) */
{
pvxiio->frame=PVXIDI(pvxiio->la)->slot_zero_la;
pvxiio->slot=PVXIDI(pvxiio->la)->slot;
}
return(pvxiio->la);
}
#define LOW_HTS 0
#define HIGH_HTS 3
static long get_enum(struct dbAddr *paddr,struct dbr_enumStrs *p)
{
struct pulseDelayRecord *ppd=(struct pulseDelayRecord *)paddr->precord;
int i,j;
Debug("Getting values for hts\n",0);
if(paddr->pfield==(void *)&ppd->hts)
{
for(j=0,i=LOW_HTS;i<=HIGH_HTS&&j<DB_MAX_CHOICES;i++,j++)
{
Debug(" strs=(%d)\n",i);
sprintf(p->strs[j],"%d",i);
}
p->no_str=j;
}
else
{
p->no_str=0;
}
return(0);
}