This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM3358: EDMA data transfer using example code in pru_software_support_package

Part Number: AM3358


Tool/software:

I am working on DMA transfer in BB Black using PRU. This code is provided in pru software support package for am335x (BB Black).

#include <stdint.h>
#include <pru_cfg.h>

volatile register uint32_t __R30;
volatile register uint32_t __R31;

/* 1D Transfer Parameters */
typedef struct {
uint32_t src;
uint32_t dst;
uint32_t chan;
} hostBuffer;

/* EDMA PARAM registers */
typedef struct {
uint32_t sam : 1;
uint32_t dam : 1;
uint32_t syncdim : 1;
uint32_t static_set : 1;
uint32_t : 4;
uint32_t fwid : 3;
uint32_t tccmode : 1;
uint32_t tcc : 6;
uint32_t : 2;
uint32_t tcinten : 1;
uint32_t itcinten : 1;
uint32_t tcchen : 1;
uint32_t itcchen : 1;
uint32_t privid : 4;
uint32_t : 3;
uint32_t priv : 1;
} edmaParamOpt;

/*typedef struct{
uint32_t src;
} edmaParamSrc;*/

typedef struct {
uint32_t acnt : 16;
uint32_t bcnt : 16;
} edmaParamABcnt;

/*typedef struct{
uint32_t dst;
} edmaParamDst;*/

typedef struct {
uint32_t srcbidx : 16;
uint32_t dstbidx : 16;
} edmaParamBidx;

typedef struct {
uint32_t link : 16;
uint32_t bcntrld : 16;
} edmaParamLnkRld;

typedef struct {
uint32_t srccidx : 16;
uint32_t dstcidx : 16;
} edmaParamCidx;

typedef struct {
uint32_t ccnt : 16;
uint32_t : 16;
} edmaParamCcnt;

typedef struct {
edmaParamOpt opt;
/*edmaParamSrc*/ uint32_t src;
edmaParamABcnt abcnt;
/*edmaParamDst*/ uint32_t dst;
edmaParamBidx bidx;
edmaParamLnkRld lnkrld;
edmaParamCidx cidx;
edmaParamCcnt ccnt;
} edmaParam;

/* This is a compiler hack so that the PRU knows where the parameters are stored
* This is effectively a pointer to DRAM
*/
#pragma LOCATION(buf, 0)
hostBuffer buf;

/* Addresses for Constant Table pointer registers
* CTBIR_0 -> C24 (PRU0 DRAM)
* CTBIR_1 -> C25 (PRU1 DRAM)
*/
#define CTBIR_0 (*(volatile uint32_t *)(0x22020))
#define CTBIR_1 (*(volatile uint32_t *)(0x22024))

/* EDMA Channel Registers */
#define CM_PER_BASE ((volatile uint32_t *)(0x44E00000))
#define TPTC0_CLKCTRL (0x24 / 4)
#define TPCC_CLKCTRL (0xBC / 4)
#define ON (0x2)

/* EDMA Channel Registers */
#define EDMA0_CC_BASE ((volatile uint32_t *)(0x49000000))
#define DMAQNUM0 (0x0240 / 4)
#define DMAQNUM1 (0x0244 / 4)
#define DCHMAP_10 (0x0128 / 4)
#define QUEPRI (0x0284 / 4)
#define EMR (0x0300 / 4)
#define EMCR (0x0308 / 4)
#define EMCRH (0x030C / 4)
#define QEMCR (0x0314 / 4)
#define CCERRCLR (0x031C / 4)
#define DRAE0 (0x0340 / 4)
#define DRAE1 (0x0348 / 4)
#define DRAE2 (0x0350 / 4)
#define DRAE3 (0x0358 / 4)
#define QWMTHRA (0x0620 / 4)
#define GLOBAL_ESR (0x1010 / 4)
#define GLOBAL_ESRH (0x1014 / 4)
#define GLOBAL_EECR (0x1028 / 4)
#define GLOBAL_EECRH (0x102C / 4)
#define GLOBAL_SECR (0x1040 / 4)
#define GLOBAL_SECRH (0x1044 / 4)
#define GLOBAL_IESR (0x1060 / 4)
#define GLOBAL_IESRH (0x1064 / 4)
#define GLOBAL_ICR (0x1070 / 4)
#define GLOBAL_ICRH (0x1074 / 4)

/* EDMA Shadow Region 1 */
#define ESR (0x2210 / 4)
#define ESRH (0x2214 / 4)
#define EESR (0x1030 / 4)
#define EECR (0x2228 / 4)
#define EECRH (0x222C / 4)
#define SECR (0x2240 / 4)
#define SECRH (0x2244 / 4)
#define IPR (0x2268 / 4)
#define IPRH (0x226C / 4)
#define ICR (0x2270 / 4)
#define ICRH (0x2274 / 4)
#define IESR (0x2260 / 4)
#define IESRH (0x2264 / 4)
#define IEVAL (0x2278 / 4)
#define IECR (0x2258 / 4)
#define IECRH (0x225C / 4)

/* EDMA PARAM registers */
#define PARAM_OFFSET (0x4000 / 4)
#define OPT 0x00
#define SRC 0x04
#define ACNT 0x100
#define BCNT 0x1
#define DST 0x0C
#define SRC_DST_BIDX 0x10
#define LINK_BCNTRLD 0x14
#define SRC_DST_CIDX 0x18
#define CCNT 0x1


#define COPY_LENGTH 32

void main(void)
{
hostBuffer hostData;
uint32_t channelMask;
uint16_t paramOffset;
edmaParam params;
volatile uint32_t *ptr;
volatile uint32_t *ptr_cm;
volatile edmaParam *pParams;

ptr = EDMA0_CC_BASE;
ptr_cm = CM_PER_BASE;

/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

ptr_cm[TPTC0_CLKCTRL] = ON;
ptr_cm[TPCC_CLKCTRL] = ON;

/* Load channel parameters from DRAM - loaded by host */
hostData.src = /*buf.src*/ 0x4A310000; //PRU Shared memory
hostData.dst = /*buf.dst*/ 0x4A310100; //PRU Shared memory
hostData.chan = /*buf.chan*/ 10;

channelMask = (1 << hostData.chan);

/* Map Channel 10 to PaRAM 10 */
ptr[DCHMAP_10] = (hostData.chan << 5);

/* Setup EDMA region access for Shadow Region 1 */
ptr[DRAE1] |= channelMask;

/* Clear channel event from EDMA event registers */
ptr[SECR] |= channelMask;
ptr[ICR] |= channelMask;

/* Enable channel interrupt */
ptr[IESR] |= channelMask;

/* Enable channel */
ptr[EESR] |= channelMask;

/* Clear event missed register */
ptr[EMCR] |= channelMask;

/* Setup channel to submit to EDMA TC0 */
ptr[DMAQNUM1] &= 0xFFFFF0FF;

/* Setup and store PaRAM set for transfer */
paramOffset = PARAM_OFFSET;
/* channel * 0x20, word address */
paramOffset += ((hostData.chan << 5) / 4);

params.lnkrld.link = 0xFFFF;
params.lnkrld.bcntrld = 0x0000;
params.opt.tcc = hostData.chan;
params.opt.tcinten = 1;
params.opt.itcchen = 1;

params.ccnt.ccnt = CCNT;
params.abcnt.acnt = ACNT;
params.abcnt.bcnt = BCNT;
params.bidx.srcbidx = 0x1;
params.bidx.dstbidx = 0x1;
params.src = hostData.src;
params.dst = hostData.dst;

pParams = (volatile edmaParam *)(ptr + paramOffset);
*pParams = params;

/* Trigger transfer */
ptr[ESR] = (channelMask);

/* Wait for transfer completion */
while (!(ptr[IPR] & channelMask)) {
}

/* Halt PRU core */
__halt();
}

What is the use of this code?
I am putting data using “sudo devmem2 0x4A310000 w 0x2”. But after running the above code (provided in pru software support package) and then checking 0x4A310100, it is blank. The value “0x2” is not present there. The outputs are provided below.

debian@BeagleBone:~$sudo devmem2 0x4A310000 (source)
/dev/mem opened.
Memory mapped at address 0xb6f33000.
Value at address 0x4A310000 (0xb6f33000): 0x2


debian@BeagleBone:~$ sudo devmem2 0x4A310100 (destination)
/dev/mem opened.
Memory mapped at address 0xb6f76000.
Value at address 0x4A310100 (0xb6f76100): 0x0


On 23/02/2025 it worked smoothly. But on 24/02/2025 it is not working.

what can be the reason? How to resolve this? 

  • Hello,

    What is the example doing? 

    I have not had time to run tests with this example yet. If you run tests and let me know how they go, we can update the edmaConfig example based on your results.

    It looks like the code is writing to the ESR register to manually trigger a DMA copy from 0x4A31_0000 (source) to 0x4A31_0100 (destination)

    	hostData.src = /*buf.src*/ 0x4A310000;	//PRU Shared memory
    	hostData.dst = /*buf.dst*/ 0x4A310100;	//PRU Shared memory

    I do not actually see any data getting written to 0x4A310000 beforehand. So if we wanted to test the code properly, we should write data to the source location, and then see if it gets copied to the destination.

    How much data is getting transferred? 

    Look at ACNT, BCNT, and CCNT:
    #define ACNT 0x100 - copy 256 bytes
    #define BCNT 0x1 - do 1 array of length ACNT (0 is not a valid value)
    #define CCNT 0x1 - do 1 frame in a block (0 is not a valid value)

    For more information, refer to the technical reference manual (TRM) sections "Count for 1st Dimension (ACNT)", "Count for 2nd Dimension (BCNT)", "Count for 3rd Dimension (CCNT)".

    I am not sure what #define COPY_LENGTH 32 is there for. The value does not seem to be used, and it does not look like we are copying 32 bits, 32 bytes, or 32 32-bit words. I might delete that line based on your feedback.

    How can we add data to PRU's shared RAM to test? 

    See PRU Getting Started Labs > Lab 5: Basic Debugging of PRU Firmware:
    https://software-dl.ti.com/processor-sdk-linux/esd/AM335X/09_03_05_02/exports/docs/common/PRU-ICSS/PRU-Getting-Started-Labs_Lab5.html

    You can either manually modify the values in memory through the CCS Memory Browser (See "Debugging the PRU from CCS" > "C Code"), or you can add a line of code to your PRU project to add values into memory (see "Debugging the PRU from Linux Core").

    You could add values at 0x4A310000, 0x4A310010, 0x4A3100FC to make sure that all values are actually getting copied to 0x4A310100, 0x4A310110, and 0x4A3100FC.

    Previous customers who used PRU DMA (and other potentially helpful resources) 

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/952427/processor-sdk-am335x-ddr-to-pru-sram-edma-transfer-on-am335x

    ^ Note discussion about disabling certain elements in the Linux devicetree

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/675887/linux-am3358-mcasp-dma-data-drops

    Some additional documentation about EDMA (note that TI_RTOS is no longer supported, so just focus on the description of the HW):

    5008.Migrating_Applications_from_EDMA_to_UDMA_using_TI-RTOS.pdf

  • This example code (PRU_edmaConfig) work with slight modification in OPT, which is provided in chapter 8 of Link

    params.opt.sam = 0;
    params.opt.dam = 0;
    params.opt.syncdim = 1;
    params.opt.static_set = 1;
    params.opt.fwid = 0;
    params.opt.tcc = 0;
    params.opt.tccmode = 0;
    params.opt.tcinten = 0;
    params.opt.itcchen = 0;
    params.opt.tcchen = 0;
    params.opt.privid = 0;
    params.opt.priv = 1;

  • Hello Nagesh,

    1) Please provide additional detail about what you mean by "working" vs "not working".

    2) syncdim = 0 or 1 should not make a difference if you are using ACNT = 256, BCNT = CCNT = 1.

    3) Additionally, I would expect your change of setting tcinten = itcchen = 0 would break the example.

    The example waits for an interrupt saying that the transfer completed. Since you disabled both intermediate transfer completion interrupt & transfer complete interrupt, I would expect that you never actually leave this while statement:

    	/* Wait for transfer completion */
    	while (!(ptr[IPR] & channelMask)) {
    	}

    Regards,

    Nick