EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: Epics DMA for the MVME6100
From: Andrew Johnson <[email protected]>
To: "Szalata, Zenon M." <[email protected]>
Cc: [email protected]
Date: Wed, 2 Jul 2008 11:27:09 -0500
Hi Zen,

On Tuesday 01 July 2008 17:34:00 Szalata, Zenon M. wrote:
> I would like a copy of your driver code and instructions on how to use it,
> if possible. Thanks for your help,

See the attached source file, which should be copied to your 
target/config/mv6100 directory.

To install, edit the sysTempe.c file and look for the line that says
	#include "sysTempeDma.c"
which is pulling in the built-in DMA interface.  Replace it with this:
	#include "tempeDma.c"

I can't promise that it will build immediately, but I can't see that it's 
relying on any of my other BSP changes.  I should also say that I use the 
command-line to build my BSPs, and I don't know what you need to do to 
install this if you use a Wind River GUI, but hopefully you'll be able to 
work that out.

You'll also want to put the following API declarations into your application:

/* VME DMA Routines from APS */

typedef struct dmaRequest *DMA_ID;

extern DMA_ID sysDmaCreate(VOIDFUNCPTR callback, void *context);
extern void * sysDmaContext(DMA_ID dmaId);
extern STATUS sysDmaStatus(DMA_ID dmaId);
extern STATUS sysDmaToVme(DMA_ID dmaId, UINT32 vmeAddr, int adrsSpace,
			  void *pLocal, int length, int dataWidth);
extern STATUS sysDmaFromVme(DMA_ID dmaId, void *pLocal, UINT32 vmeAddr,
			    int adrsSpace, int length, int dataWidth);


Usage should be fairly easy to follow and follows common vxWorks conventions.  
First register a completion callback and create a DMA_ID object using 
sysDmaCreate(), then pass that DMA_ID into the other routines to start off an 
asynchronous DMA transfer [sysDmaToVme() or sysDmaFromVme()], to check its 
status [sysDmaStatus()] or to get back the context pointer you passed in when 
you created it [sysDmaContext() â this lets you recover information about the 
transfer later].  Your callback will be called from the interrupt that occurs 
when the DMA is finished, and is passed your context pointer as the only 
argument.  The usual cautions apply as to what you're allowed to call from an 
ISR.  The status value returned by the sysDmaStatus() routine is OK when the 
DMA is complete or ERROR if not; it sets errno before returning ERROR so you 
can distinguish between EFAULT, ECANCELLED and EINPROGRESS.

It seems there's no API to actually abort an ongoing DMA, and when you're done 
with a DMA_ID you can just pass it to free().

There is an epicsDma wrapper for this API inside the Generic Transient 
Recorder application which also provides an implementation of the DMA driver 
for the Universe-2 chip, although that's really aimed at RTEMS.

HTH,

- Andrew
-- 
Talk is cheap. Show me the code. -- Linus Torvalds
/* tempeDma.c - Tundra Tsi148 chip DMA interface library */

/*
DESCRIPTION

This driver replaces the DMA routines in the WRS BSP, to provide
a uniform BSP interface on all the APS boards.

*/

/* inlines */


/* defines */

#define DMA_MAGIC	0x444D414D
#define DMA_TIMEOUT	10	/* Yuck, ticks */

#define DCTL_GO_BITS	(	\
	TEMPE_DCTLx_DGO_MASK  |	\
	TEMPE_DCTLx_VBKS_MASK |	\
	TEMPE_DCTLx_PBKS_MASK	\
	)

/* These should probably be in tempe.h, but they aren't */

#define DxAT_TYP_PCI	0
#define DxAT_TYP_VME	(1 << 28)

#define DxAT_TM_MASK	(7 << 8)
#define DxAT_TM_SCT	0
#define DxAT_TM_BLT	(1 << 8)
#define DxAT_TM_MBLT	(2 << 8)
#define DxAT_TM_2eVME	(3 << 8)
#define DxAT_TM_2eSST	(4 << 8)
#define DxAT_TM_2eSSTB	(5 << 8)

#define DxAT_DBW_16	0
#define DxAT_DBW_32	(1 << 6)

#define DxAT_SUP_USR	0
#define DxAT_SUP_SUP	(1 << 5)

#define DxAT_PGM_DAT	0
#define DxAT_PGM_PGM	(1 << 4)

#define DxAT_AM_MASK	(15 << 0)
#define DxAT_AM_A16	0
#define DxAT_AM_A24	(1 << 0)
#define DxAT_AM_A32	(2 << 0)
#define DxAT_AM_A64	(4 << 0)
#define DxAT_AM_CRCSR	(5 << 0)
#define DxAT_AM_USR1	(8 << 0)
#define DxAT_AM_USR2	(9 << 0)
#define DxAT_AM_USR3	(10 << 0)
#define DxAT_AM_USR4	(11 << 0)


/* types */

struct dmaRequest {
    UINT32 dsau;		/* DMAC Linked-List Descriptor */
    UINT32 dsal;
    UINT32 ddau;
    UINT32 ddal;
    UINT32 dsat;
    UINT32 ddat;
    UINT32 dnlau;
    UINT32 dnlal;
    UINT32 dcnt;
    UINT32 ddbs;
    int magic;			/* For checking request arg's */
    VOIDFUNCPTR callback;	/* Completion function */
    void *context;		/* User's argument */
    int timeout;		/* Maximum start delay */
    UINT32 dsta;		/* Status bits */
    struct dmaRequest *next;	/* Request queue */
};

typedef struct dmaRequest *DMA_ID;


/* Forward declarations */

void sysDmaInt(int dummy);
LOCAL STATUS dmaStart(DMA_ID dmaId);


/* externals */

IMPORT BOOL isPciAdrs(char *adrs, char **pciTrans);


/* Private data */

LOCAL SEM_ID dmaOwner = 0;
LOCAL DMA_ID dmaCurrent[2];
LOCAL DMA_ID dmaChain;

/******************************************************************************
*
* sysDmaInit - Inititalize the Tempe's DMA engine
*
* This routine initializes the library and connects up the Tempe's DMA
* interrupts.
* 
* RETURNS: OK or ERROR
*/

STATUS sysDmaInit
    (
    void
    )
    {
    if (dmaOwner) return OK;

    dmaOwner = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);

    /* Connect ISR */
    if (dmaOwner == NULL ||
	intConnect(INUM_TO_IVEC(TEMPE_INT_NUM_DMA0), sysDmaInt, 0) ||
	intConnect(INUM_TO_IVEC(TEMPE_INT_NUM_DMA1), sysDmaInt, 1) ||
	intEnable(TEMPE_INT_NUM_DMA0) ||
	intEnable(TEMPE_INT_NUM_DMA1))
	return ERROR;

    puts("Warning: The VME DMA interface on the mv6100 BSP is not tested!");
    return OK;
    }


/******************************************************************************
*
* sysDmaCreate - Create and initialize a DMA request
*
* This routine creates a DMA request object and sets the callback
* function and parameter to call when the DMA action completes.
* The callback function may be NULL.
*
* RETURNS: New DMA request ID, or NULL if insufficient memory.
*/

DMA_ID sysDmaCreate
    (
    VOIDFUNCPTR callback,	/* Completion callback routine */
    void *context		/* Argument passed to callback */
    )
    {
    DMA_ID dmaId;

    if (sysDmaInit()) return NULL;

    dmaId = (DMA_ID) memalign(64, sizeof (struct dmaRequest));
    if (dmaId != NULL)
	{
	dmaId->dsau     = 0;
	dmaId->ddau     = 0;
	dmaId->dnlal    = TEMPE_DNLALx_LLA_MASK;
	dmaId->dnlau    = 0;
	dmaId->ddbs     = 0;
	dmaId->magic    = DMA_MAGIC;
	dmaId->callback = callback;
	dmaId->context  = context;
	dmaId->dsta     = TEMPE_DSTAx_DON_MASK;
	dmaId->timeout  = DMA_TIMEOUT;
	dmaId->next     = NULL;
	}
    return dmaId;
    }


/******************************************************************************
*
* sysDmaContext - Return user context for a DMA request
*
* This routine returns the user context value which was passed into
* the sysDmaCreate() function that created the DMA request.
*
* RETURNS: The user context value.
*/

void * sysDmaContext
    (
    DMA_ID dmaId	/* DMA request containing context */
    )
    {
    return dmaId->context;
    }


/******************************************************************************
*
* sysDmaStatus - Check completion of a DMA request
*
* This routine checks the status of a DMA request and signals any errors
* that occurred during the transfer.
*
* RETURNS: OK, or ERROR if the operation hasn't finished or failed.
*/

STATUS sysDmaStatus
    (
    DMA_ID dmaId	/* DMA request to check */
    )
    {
    UINT32 dsta = dmaId->dsta & (
		  TEMPE_DSTAx_ERR_MASK |
		  TEMPE_DSTAx_ABT_MASK |
		  TEMPE_DSTAx_DON_MASK
		  );
    if (dsta & TEMPE_DSTAx_DON_MASK)
	return OK;

    /* The order of these tests might be wrong if multiple bits are set: */
    if (dsta & TEMPE_DSTAx_ABT_MASK)
	errno = ECANCELED;
    else if (dsta & TEMPE_DSTAx_ERR_MASK)
	errno = EFAULT;
    else
	errno = EINPROGRESS;
    return ERROR;
    }


/******************************************************************************
*
* vmeAddrToDxAT - find attribute value
*
* This routine range-checks the VME address and calculates the correct
* setting for the DxAT register based on the address space and
* data width requested.
*
* RETURNS: DxAT register value, or 0 if request invalid.
*
* NOMANUAL
*/

LOCAL UINT32 vmeAddrToDxAT
    (
    UINT32 vmeAddr,	/* VME bus address */
    int adrsSpace,	/* bus address modifier */
    int dataWidth	/* 1, 2, 4 or 8 bytes per VME cycle */
    )
    {
    UINT32 dxatValue;
    static const UINT32 dxat[0x40] =
	{
/*00*/	0, 0, 0, 0, 0, 0, 0, 0,	/* A64 modes not supported */
/*08*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_USR | DxAT_TM_MBLT,
/*09*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_USR | DxAT_PGM_DAT | DxAT_TM_SCT,
/*0a*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_USR | DxAT_PGM_PGM | DxAT_TM_SCT,
/*0b*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_USR | DxAT_TM_BLT,
/*0c*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_SUP | DxAT_TM_MBLT,
/*0d*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_SUP | DxAT_PGM_DAT | DxAT_TM_SCT,
/*0e*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_SUP | DxAT_PGM_PGM | DxAT_TM_SCT,
/*0f*/	DxAT_TYP_VME | DxAT_AM_A32 | DxAT_SUP_SUP | DxAT_TM_BLT,
/*10*/	0, 0, 0, 0, 0, 0, 0, 0,	/* User-defined, not supported */
/*18*/	0, 0, 0, 0, 0, 0, 0, 0,	/* User-defined, not supported */
/*20*/	0, 0, 0, 0, 0, 0, 0, 0,
/*28*/	0,
/*29*/	DxAT_TYP_VME | DxAT_AM_A16 | DxAT_SUP_USR | DxAT_PGM_DAT | DxAT_TM_SCT,
/*2a*/	0, 0, 0,
/*2d*/	DxAT_TYP_VME | DxAT_AM_A16 | DxAT_SUP_SUP | DxAT_PGM_DAT | DxAT_TM_SCT,
/*2e*/	0, 0,
/*30*/	0, 0, 0, 0, 0, 0, 0, 0,
/*38*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_USR | DxAT_TM_MBLT,
/*39*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_USR | DxAT_PGM_DAT | DxAT_TM_SCT,
/*3a*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_USR | DxAT_PGM_PGM | DxAT_TM_SCT,
/*3b*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_USR | DxAT_TM_BLT,
/*3c*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_SUP | DxAT_TM_MBLT,
/*3d*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_SUP | DxAT_PGM_DAT | DxAT_TM_SCT,
/*3e*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_SUP | DxAT_PGM_PGM | DxAT_TM_SCT,
/*3f*/	DxAT_TYP_VME | DxAT_AM_A24 | DxAT_SUP_SUP | DxAT_TM_BLT
	};

    if ((adrsSpace < 0) || (adrsSpace >= 64))
	goto err;

    dxatValue = dxat[adrsSpace];
    if (dxatValue == 0)
	goto err;

    switch (dxatValue & DxAT_AM_MASK) {
    case DxAT_AM_A16:
	if (vmeAddr > 0xffff)
	    goto err;
	break;
	
    case DxAT_AM_A24:
	if (vmeAddr > 0xffffff)
	    goto err;
	break;
    }

    switch (dataWidth) {
    case 2:
	if ((dxatValue & DxAT_TM_MASK) > DxAT_TM_BLT)
	    goto err;
	dxatValue |= DxAT_DBW_16;
	break;
	
    case 4:
	if ((dxatValue & DxAT_TM_MASK) > DxAT_TM_BLT)
	    goto err;
	dxatValue |= DxAT_DBW_32;
	break;
	
    case 8:
	if ((dxatValue & DxAT_TM_MASK) < DxAT_TM_MBLT)
	    goto err;
	break;
	
    default:
	goto err;
    }

    return dxatValue;

err:
    errno = EINVAL;
    return 0;
    }


/******************************************************************************
*
* sysDmaToVme - DMA data to VME
*
* This routine starts a DMA transfer from local memory to a VME location.
*
* RETURNS: OK, or ERROR if the transfer request is invalid.
*/

STATUS sysDmaToVme
    (
    DMA_ID dmaId,	/* DMA request to use */
    UINT32 vmeAddr,	/* VME address of destination */
    int adrsSpace,	/* VME address modifier */
    void *pLocal,	/* pointer to source in local memory */
    int length,		/* number of bytes to transfer */
    int dataWidth	/* 1, 2, 4 or 8 bytes per VME write cycle */
    )
    {
    if (dmaId->magic != DMA_MAGIC)
	{
	errno = S_objLib_OBJ_ID_ERROR;
	return ERROR;
	}

    if (dmaId->dsta & TEMPE_DSTAx_BSY_MASK)
	{
	errno = EBUSY;
	return ERROR;
	}

    if (!isPciAdrs((char *) pLocal, (char **) &dmaId->dsal))
	{
	errno = EINVAL;
	return ERROR;
	}

    if ((dmaId->ddat = vmeAddrToDxAT(vmeAddr, adrsSpace, dataWidth)) == 0)
	return ERROR;

    dmaId->dsat = DxAT_TYP_PCI;
    dmaId->ddal = vmeAddr;
    dmaId->dcnt = length;

    return dmaStart(dmaId);
    }


/******************************************************************************
*
* sysDmaFromVme - DMA data from VME
*
* This routine starts a DMA transfer from a VME location to local memory.
*
* RETURNS: OK, or ERROR if the transfer request is invalid.
*/

STATUS sysDmaFromVme
    (
    DMA_ID dmaId,	/* DMA request to use */
    void *pLocal,	/* pointer to destination in local memory */
    UINT32 vmeAddr,	/* VME address of source */
    int adrsSpace,	/* VME address modifier */
    int length,		/* number of bytes to transfer */
    int dataWidth	/* 1, 2, 4 or 8 bytes per VME read cycle */
    )
    {
    if (dmaId->magic != DMA_MAGIC)
	{
	errno = S_objLib_OBJ_ID_ERROR;
	return ERROR;
	}

    if (dmaId->dsta & TEMPE_DSTAx_BSY_MASK)
	{
	errno = EBUSY;
	return ERROR;
	}

    if ((dmaId->dsat = vmeAddrToDxAT(vmeAddr, adrsSpace, dataWidth)) == 0)
	return ERROR;

    if (!isPciAdrs((char *) pLocal, (char **) &dmaId->ddal))
	{
	errno = EINVAL;
	return ERROR;
	}

    dmaId->dsal = vmeAddr;
    dmaId->ddat = DxAT_TYP_PCI;
    dmaId->dcnt = length;

    return dmaStart(dmaId);
    }


/******************************************************************************
*
* dmaStart - start or queue DMA action
*
* This routine queues the given DMA request to the Tempe.
*
* RETURNS: OK, or ERROR if given 
*
* NOMANUAL
*/

LOCAL STATUS dmaStart
    (
    DMA_ID dmaId	/* DMA request to start */
    )
    {
    UINT32 base = TEMPE_REG_BASE;
    char * dmaPci;
    int channel;
    DMA_ID chain;

    /* Convert local address to PCI memory space */
    if (!isPciAdrs((char *) dmaId, &dmaPci))
	{
	errno = EINVAL;
	return ERROR;
	}

    dmaId->dsta = TEMPE_DSTAx_BSY_MASK; /* Mark this request in use */

    cacheFlush(DATA_CACHE, (void *)dmaId, sizeof(struct dmaRequest));

    /* Grab the mutex */
    if (semTake(dmaOwner, dmaId->timeout))
	return ERROR;

    /* Disable DMA interrupts */
    sysTempeIntChange(0, (1 << TEMPE_INT_NUM_DMA0_BIT) ||
	(1 << TEMPE_INT_NUM_DMA1_BIT));

    for (channel=0; channel<2; channel++)
	{
	if (dmaCurrent[channel] == NULL)
	    {
	    dmaCurrent[channel] = dmaId;
	    TEMPE_WRITE32_PUSH(base, TEMPE_DNLAL(channel), (UINT32) dmaPci);
	    TEMPE_WRITE32_PUSH(base, TEMPE_DCTL(channel), DCTL_GO_BITS);
	    goto done;
	    }
	}

    /* Both channels are busy, queue this request */
    if ((chain = dmaChain) == NULL)
	dmaChain = dmaId;
    else
	{
	DMA_ID next;
	while ((next = chain->next) != NULL)
	    {
	    chain = next;
	    }
	chain->next = dmaId;	/* Append this request */
	}

done:
    /* Re-enable DMA interrupts */
    sysTempeIntChange((1 << TEMPE_INT_NUM_DMA0_BIT) ||
	(1 << TEMPE_INT_NUM_DMA1_BIT), 0);

    semGive(dmaOwner);
    return OK;
    }


/******************************************************************************
*
* sysDmaInt - DMA Interrupt Service routine
*
* This routine acknowledges a completed DMA action, and if there is one
* queued it sends the next DMA request to this controller. We can't let
* the controllers free-run as they only interrupt at the end of their
* DMA list (however if someone needs scatter/gather operations it should
* be relatively easy to combine several linked operations into a single
* DMA_ID request).
*
* RETURNS: N/A
*
* NOMANUAL
*/

void sysDmaInt
    (
    int channel
    )
    {
    UINT32 base = TEMPE_REG_BASE;
    DMA_ID done = dmaCurrent[channel];

    done->dsta = TEMPE_READ32(base, TEMPE_DSTA(channel));

    dmaCurrent[channel] = dmaChain;
    if (dmaChain != NULL)
	{
	char * dmaPci;
	
	isPciAdrs((char *) dmaChain, &dmaPci);
	dmaChain = dmaChain->next;
	TEMPE_WRITE32_PUSH(base, TEMPE_DNLAL(channel), (UINT32) dmaPci);
	TEMPE_WRITE32_PUSH(base, TEMPE_DCTL(channel), DCTL_GO_BITS);
	}

    if (done->callback)
	done->callback(done->context);
    }


References:
Epics DMA for the MVME6100 Szalata, Zenon M.
Re: Epics DMA for the MVME6100 Andrew Johnson
RE: Epics DMA for the MVME6100 Szalata, Zenon M.

Navigate by Date:
Prev: CAEN VME V1724 8 Channel 14 bit 100 MS/s Digitizer Rose, AJ (Austen)
Next: CAC: unable to create virtual circuit b/c "Too many open files" Bertrand H.J. Biritz
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: RE: Epics DMA for the MVME6100 Szalata, Zenon M.
Next: CAEN VME V1724 8 Channel 14 bit 100 MS/s Digitizer Rose, AJ (Austen)
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 02 Sep 2010 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·