EPICS Home

Experimental Physics and Industrial Control System


 
1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  <20042005  2006  2007  2008  2009  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  <20042005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: gtr1-2 for SIS3301.
From: Andrew Johnson <[email protected]>
To: Kiman Ha <[email protected]>
Cc: [email protected]
Date: Fri, 27 Feb 2004 11:01:57 -0600
Kiman Ha wrote:

Undefined symbol: sysDmaStatus (binding 1 type 0) Undefined symbol: sysDmaToVme (binding 1 type 0) Undefined symbol: sysDmaCreate (binding 1 type 0) Undefined symbol: sysDmaFromVme (binding 1 type 0)

These symbols are the generic API for a VMEbus DMA controller which I created, and which I have implemented on both the VMEchip2 (Motorola 68K CPUs) and the Tundra Universe-2 (Motorola PowerPC CPUs). There is an API in the mv5100 BSP for controlling the Tundra DMA controller, but it's not thread safe, doesn't support completion interrupts, and is very Universe specific, so I wrote my own generic one. The API looks like this:


/* DMA Routines */

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);


To add this facility to your mv5100 BSP:


1. Save the attached file univDma.c to your <tornado>/target/config/mv5100 directory.

2. Add this line to the sysLib.c file, just after #include "universe.c"
	#include "univDma.c"

3. Make sure that INCLUDE_VME_DMA is *not* defined anywhere, which excludes the WRS-provided DMA driver code.

4. Apply this patch to the universe.h file, then rebuild the BSP:

--- universe.h-orig     2002-06-14 21:05:23.000000000 -0500
+++ universe.h  2003-11-17 13:11:05.856449000 -0600
@@ -589,6 +589,14 @@
 #define DTBC_MASK       0xff000000
 #define        DTBC_VALID_BITS_MASK    0x00ffffff

+/* DMA Command Packet Pointer Register */
+
+#define DCPP_VALID_BITS_MASK   0xffffffe0
+
+/* In-memory DCPP bits for linked-list mode */
+#define DCPP_PROCESSED         (1 << 1)
+#define DCPP_NULL              (1 << 0)
+
 /* DMA General Control/Status Register */

 #define DGCS_MASK               0x00800000      /* Reserved bits */
@@ -636,5 +644,8 @@

/* DMA Linked List Update Enable Register */

+#define D_LLUE_UPDATE          (1 << 31)
+#define D_LLUE_UNLOCK          (0)
+
 /* PCI Configuration Base Address Register */


I hope I didn't miss anything, let me know if it doesn't compile properly.


- Andrew
--
Dear God, I didn't think orange went with purple until I saw
the sunset you made last night.  That was really cool. - Caro
/* univDma.c - Tundra Universe chip DMA interface library */

/*
DESCRIPTION

This driver replaces the DMA routines in the WRS BSP which are not
thread-safe and don't support completion interrupts.

*/

/* inlines */

#if (CPU_FAMILY == PPC)

static __inline__ void put_swap(UINT32 *pdst, UINT32 val) {
    __asm__ volatile ("stwbrx %1, 0, %0" : : "r" (pdst), "r" (val));
}
#define PUT_SWAP(dst, val) put_swap((UINT32 *)(&(dst)), (val))

static __inline__ UINT32 get_swap(UINT32* src) {
    UINT32 ret;
    __asm__ volatile ("lwbrx %0, 0, %1" : "=r" (ret) : "r" (src));
    return ret;
}
#define GET_SWAP(src) get_swap((UINT32 *)(&(src)))

#else
#error "univDma.c doesn't support this processor family"
/* because of the above inline assembler routines... */
#endif /* (CPU_FAMILY == ...) */


/* defines */

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


/* types */

struct dmaRequest {
    UINT32 dctl;		/* DMAC chain packet registers, PCI order */
    UINT32 dtbc;
    UINT32 dla;
    UINT32 reserved_1;
    UINT32 dva;
    UINT32 reserved_2;
    UINT32 dcpp;
    UINT32 reserved_3;
    int magic;			/* For checking request arg's */
    VOIDFUNCPTR callback;	/* Completion function */
    void *context;		/* User's argument */
    int timeout;		/* Maximum start delay */
    UINT32 dgcs;		/* Control/Status values */
};

typedef struct dmaRequest *DMA_ID;


/* Forward declarations */

static void dmaIsr(int dummy);
static STATUS adrsToDctl(DMA_ID dmaId, UINT32 vmeAddr, int adrsSpace, int dataWidth);
static STATUS dmaStart(DMA_ID dmaId);


/* externals */

IMPORT STATUS sysLocalToPciAdrs(int adrsSpace, void *adrs, UINT32 *pPciAdrs);
IMPORT void sysUsDelay(int delay);


/* Private data */

static SEM_ID dmaOwner = 0;
static DMA_ID dmaCurrent;


/******************************************************************************
*
* sysDmaInit - Inititalize the Universe's DMA engine
*
* This routine initializes the library and sets up the Universe's DMA
* registers.
* 
* RETURNS: OK
*/

STATUS sysDmaInit
    (
    void
    )
    {
    if (dmaOwner) return OK;
    
    dmaOwner = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
    
    /* Connect ISR */
    if (sysUnivIntConnect(UNIVERSE_DMA_INT, dmaIsr, 0))
	return ERROR;
    
    return sysUnivIntEnable(UNIVERSE_DMA_INT);
    }


/******************************************************************************
*
* 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 or
* interrupt connection failed.
*/

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(32, sizeof (struct dmaRequest));
    if (dmaId)
	{
	dmaId->magic    = DMA_MAGIC;
	dmaId->callback = callback;
	dmaId->context  = context;
	dmaId->dgcs     = DGCS_CHAIN_LLMODE | DGCS_VON_DONE | DGCS_VOFF_0 |
			  DGCS_INT_STOP | DGCS_INT_HALT | DGCS_INT_DONE |
			  DGCS_INT_LERR | DGCS_INT_VERR | DGCS_INT_P_ERR |
			  DGCS_DONE;	/* Ensure an OK initial status */
	dmaId->timeout  = DMA_TIMEOUT;
	}
    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 dgcs = dmaId->dgcs &
		  (DGCS_DONE | DGCS_LERR | DGCS_VERR | DGCS_P_ERR | DGCS_STOP);
    if (dgcs & DGCS_DONE)
	return OK;
    
    /* The order of these tests might be wrong if multiple bits are set: */
    if (dgcs & DGCS_P_ERR)
	errno = EPROTO;
    else if (dgcs & DGCS_STOP)
	errno = ECANCELED;
    else if (dgcs & (DGCS_LERR | DGCS_VERR))
	errno = EFAULT;
    else
	errno = EINPROGRESS;
    return ERROR;
    }


/******************************************************************************
*
* dmaToRegs - Convert DMA arguments to Universe register values
*
* This routine converts its generic arguments into settings suitable
* for the Universe-2 register set, stored in the DMA_ID object.
* The PCI side is always configured for 64-bit reads to maximize
* throughput.
*
* RETURNS: OK, or ERROR if the transfer request is invalid.
*
* NOMANUAL
*/

static STATUS dmaToRegs
    (
    DMA_ID dmaId,	/* DMA request to use */
    UINT32 vmeAddr,	/* VME destination bus address */
    int adrsSpace,	/* bus 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 */
    )
    {
    UINT32 value;
    
    if (dmaId->magic != DMA_MAGIC)
	{
	errno = S_objLib_OBJ_ID_ERROR;
	return ERROR;
	}
    
    if (dmaId->dgcs & DGCS_ACT)
	{
	errno = EBUSY;
	return ERROR;
	}
    
    /* Check address, handle address modifier and data width */
    if (adrsToDctl(dmaId, vmeAddr, adrsSpace, dataWidth))
	return ERROR;
    
    /* Convert local address to PCI memory space */
    if (sysLocalToPciAdrs(PCI_BAR_SPACE_MEM, pLocal, &value))
	return ERROR;
    PUT_SWAP(dmaId->dla, value);
    
    /* Addresses must be 8-byte aligned with each other */
    if ((vmeAddr & 7) != (value & 7))
	{
	errno = EFAULT;
	return ERROR;
	}
    PUT_SWAP(dmaId->dva, vmeAddr);
    
    /* Maximum 16MB transfer */
    if (length > DTBC_VALID_BITS_MASK)
	{
	errno = EINVAL;
	return ERROR;
	}
    
    PUT_SWAP(dmaId->dtbc, length);
    PUT_SWAP(dmaId->dcpp, DCPP_NULL);	/* Stop chain when done */
    
    return OK;
    }


/******************************************************************************
*
* adrsToDctl - find DCTL value
*
* This routine range-checks the VME address and calculates the correct
* setting for the UNIVERSE_DCTL register based on the address space and
* data width requested.  Note that the DCTL value returned has not been
* byte-swapped, which must be done before the Universe sees the packet.
*
* RETURNS: OK, or ERROR if the transfer request is invalid.
*
* NOMANUAL
*/

static STATUS adrsToDctl
    (
    DMA_ID dmaId,	/* DMA request to use */
    UINT32 vmeAddr,	/* VME bus address */
    int adrsSpace,	/* bus address modifier */
    int dataWidth	/* 1, 2, 4 or 8 bytes per VME cycle */
    )
    {
    UINT32 dctlValue;
    static const UINT32 dctl[0x40] =
	{
/*00*/	0, 0, 0, 0, 0, 0, 0, 0,	/* A64 modes not supported */
/*08*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_USER | DCTL_VCT_BLK,
/*09*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_USER | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*0a*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_USER | DCTL_PGM_PRGM | DCTL_VCT_SINGLE,
/*0b*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_USER | DCTL_VCT_BLK,
/*0c*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_SUP | DCTL_VCT_BLK,
/*0d*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_SUP | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*0e*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_SUP | DCTL_PGM_PRGM | DCTL_VCT_SINGLE,
/*0f*/	DCTL_LD64EN | DCTL_VAS_A32 | DCTL_SUPER_SUP | DCTL_VCT_BLK,
/*10*/	0, 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0, 0, 0, 0,	/* User-defined */
/*20*/	0, 0, 0, 0, 0, 0, 0, 0,	0,
/*29*/	DCTL_LD64EN | DCTL_VAS_A16 | DCTL_SUPER_USER | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*2a*/	0, 0, 0,
/*2d*/	DCTL_LD64EN | DCTL_VAS_A16 | DCTL_SUPER_SUP | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*2e*/	0, 0,
/*30*/	0, 0, 0, 0, 0, 0, 0, 0,
/*38*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_USER | DCTL_VCT_BLK,
/*39*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_USER | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*3a*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_USER | DCTL_PGM_PRGM | DCTL_VCT_SINGLE,
/*3b*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_USER | DCTL_VCT_BLK,
/*3c*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_SUP | DCTL_VCT_BLK,
/*3d*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_SUP | DCTL_PGM_DATA | DCTL_VCT_SINGLE,
/*3e*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_SUP | DCTL_PGM_PRGM | DCTL_VCT_SINGLE,
/*3f*/	DCTL_LD64EN | DCTL_VAS_A24 | DCTL_SUPER_SUP | DCTL_VCT_BLK
	};
    
    if ((adrsSpace < 0) || (adrsSpace >= 64))
	goto err;
    
    dctlValue = dctl[adrsSpace];
    if (dctlValue == 0)
	goto err;
    
    switch (dctlValue & DCTL_VAS_MSK) {
    case DCTL_VAS_A16:
	if (vmeAddr > 0xffff)
	    goto err;
	break;
	
    case DCTL_VAS_A24:
	if (vmeAddr > 0xffffff)
	    goto err;
	break;
    }
    
    switch (dataWidth) {
    case 1:
	if ((dctlValue & DCTL_VDW_MSK) == DCTL_VDW_64)
	    goto err;
	dctlValue |= DCTL_VDW_8;
	break;
	
    case 2:
	if ((dctlValue & DCTL_VDW_MSK) == DCTL_VDW_64)
	    goto err;
	dctlValue |= DCTL_VDW_16;
	break;
	
    case 4:
	if ((dctlValue & DCTL_VDW_MSK) == DCTL_VDW_64)
	    goto err;
	dctlValue |= DCTL_VDW_32;
	break;
	
    case 8:
	if ((dctlValue & DCTL_VCT_MSK) != DCTL_VCT_BLK)
	    goto err;
	dctlValue |= DCTL_VDW_64;
	break;
	
    default:
	goto err;
    }
    
    dmaId->dctl = dctlValue;	/* NB: Unswapped value */
    return OK;
    
err:
    errno = EINVAL;
    return ERROR;
    }
    

/******************************************************************************
*
* 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 (dmaToRegs(dmaId, vmeAddr, adrsSpace, pLocal, length, dataWidth))
	return ERROR;
    
    PUT_SWAP(dmaId->dctl, dmaId->dctl | DCTL_L2V_PCI_VME);
    
    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 (dmaToRegs(dmaId, vmeAddr, adrsSpace, pLocal, length, dataWidth))
	return ERROR;
    
    PUT_SWAP(dmaId->dctl, dmaId->dctl | DCTL_L2V_VME_PCI);
    
    return dmaStart(dmaId);
    }


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

static STATUS dmaStart
    (
    DMA_ID dmaId	/* DMA request to start */
    )
    {
    UINT32 value;
    UINT32 dcpp;
    DMA_ID chain;
    
    /* Mark packet as in use */
    dmaId->dgcs &= ~(DGCS_DONE | DGCS_LERR | DGCS_VERR | DGCS_P_ERR | DGCS_STOP);
    
    /* Convert local address to PCI memory space */
    if (sysLocalToPciAdrs(PCI_BAR_SPACE_MEM, (void *) dmaId, &dcpp))
	return ERROR;
    
    if (semTake(dmaOwner, dmaId->timeout))
	return ERROR;
    
    dmaId->dgcs |= DGCS_ACT;	/* Flag this request as in use */
    
    UNIV_OUT_LONG(UNIVERSE_D_LLUE, D_LLUE_UPDATE);
    UNIV_IN_LONG(UNIVERSE_D_LLUE, &value);
    while ((value & D_LLUE_UPDATE) == 0)
	{
	sysUsDelay(10);		/* NB: Busy-wait */
	UNIV_OUT_LONG(UNIVERSE_D_LLUE, D_LLUE_UPDATE);
	UNIV_IN_LONG(UNIVERSE_D_LLUE, &value);
	}
    
    /* We now own the packet chain; the Universe-2 will stop before
     * reading the next packet and wait to be unlocked.  DMA operations
     * will continue until the current action is finished.  End-of-DMA
     * interrupts are also suspended even if this is the last packet.
     */
    
    chain = (DMA_ID) UNIVERSE_DCTL;
    while ((value = GET_SWAP(chain->dcpp)) & DCPP_VALID_BITS_MASK)
	{
	chain = (DMA_ID) (value & DCPP_VALID_BITS_MASK);
	}
    
    PUT_SWAP(chain->dcpp, dcpp | value);	   /* Queue request */
    UNIV_IN_LONG(UNIVERSE_DGCS, &value);	   /* Read DMAC state */
    UNIV_OUT_LONG(UNIVERSE_D_LLUE, D_LLUE_UNLOCK); /* Restart chain */
    
    if (!(value & DGCS_ACT))
	{			/* DMAC was idle, we have to start it */
	dmaCurrent = dmaId;
	UNIV_OUT_LONG(UNIVERSE_DTBC, 0);
	UNIV_OUT_LONG(UNIVERSE_DGCS, dmaId->dgcs | DGCS_GO);
	}
    
    semGive(dmaOwner);
    return OK;
    }


/******************************************************************************
*
* dmaIsr - DMA Interrupt Service routine
*
* This routine acknowledges a completed DMA action, and if there is one
* it starts the next DMA request to the Universe.  We don't let it free-
* run it only generates an interrupt when it reaches the end of the DMA
* list, which would delay our callbacks.
*
* RETURNS: N/A
*
* NOMANUAL
*/

static void dmaIsr
    (
    int dummy
    )
    {
    DMA_ID finished = dmaCurrent;
    UINT32 value;
    
    UNIV_IN_LONG(UNIVERSE_DGCS, &value);
    UNIV_OUT_LONG(UNIVERSE_DGCS, value);	/* Acknowledge */
    finished->dgcs = value;			/* Save status */
    
    if (value & (DGCS_LERR | DGCS_VERR | DGCS_P_ERR | DGCS_STOP))
	UNIV_OUT_LONG(UNIVERSE_DTBC, 0);	/* Clear count */
    
    if ((value = GET_SWAP(finished->dcpp)) & DCPP_VALID_BITS_MASK)
	{
	dmaCurrent = (DMA_ID) value;
	UNIV_OUT_LONG(UNIVERSE_DGCS, dmaCurrent->dgcs | DGCS_GO);
	}
    
    if (finished->callback)
	finished->callback(finished->context);
    }


Navigate by Date:
Prev: Re: print from interrupt D. Peter Siddons
Next: Re: print from interrupt Eric Norum
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  <20042005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: print from interrupt Allan Honey
Next: Re: gtr1-2 for SIS3301. Andrew Johnson
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  <20042005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024