Memory Management on ARM CORTEX-A9

Hi all,

I’m trying to get a DMA engine to access main memory on a Xilinx Zynq 7000 (ARM Cortex-A9). I’m setting the source address for the DMA engine to the start address of the array in memory. However, I’m getting bad data passed to the destination.

I understand there is no memory management in FreeRTOS, but the ARM does have a memory management unit and I read some conflicting reports on this.
Do I need to get a physical address for the DMA source address somehow?

It is unlikely the MMU is in use if FreeRTOS is running on the device, so as far as I know, physical addresses are all you have.

Thanks, Richard.

I have this code:

uint32_t E1vector[2160];
E1vector = (uint32_t *)malloc(2160*sizeof(uint32_t));
wdata = (uint32_t)&E1vector;
cdma0_srcaddr_wr(wdata);

I can breakpoint on the wdata assignment and see the what the address is, butI can’t read the elf file in Vitis to determine if it’s correct.

I know my overall programming of the DMA is correct because if I transfer the array to BRAM, it works.

The map file, generated by the linker, should give you valid address ranges.

Does the DMA work in a bare metal application (without FreeRTOS)?

I don’t see a map file from Vitis. It must be there, but I don’t see it.

I haven’t tried this in bare metal.

If I recall correctly the -Mmap command line option to GCC is used to generate a map, if there isn’t a check box for this in Vitis. You may need to supply a filename too, not sure.

I recommend trying bare metal too as a quick way to know if there is something to do with FreeRTOS’s used of the interrupt controller or not. If it is, I recommend setting up the DMA after starting the FreeRTOS scheduler.

This code snippet does not seem correct to me or am I missing something? Should it not be:

uint32_t * E1vector = (uint32_t *)malloc(2160*sizeof(uint32_t));
cdma0_srcaddr_wr( (uint32_t)E1vector ); /* Added the explicit cast because I assume this function takes a uint32_t. */

I tried this in bare metal and the results are the same. There must be a mapping occurring when going through the S_AXI_HP0 port on the Zynq PS.

I tried your code and it hangs the system. I needed to add “&” to E1vector so it doesn’t hang. But, it still doesn’t work.

Can you break in the debugger and see what it is doing when it appears hang?

What is the signature of cdma0_srcaddr_wr and what does it expect?

Okay, after a lot of digging around, I got it to work with these changes:

volatile uint32_t E1vector[2160] __attribute__ ((aligned(32)));

wdata = (uint32_t)E1vector;
cdma0_srcaddr_wr(wdata);
Xil_DCacheDisable();

I think the data cache disable is the main reason it is working. I’m hoping this doesn’t hurt performance too much!

Thank you for reporting back.

It is possible to mark areas of memory as non-cacheable in 1 Meg blocks (aligned on 1 Meg boundaries. The call is Xil_SetTlbAttributes(), declared in xil_mmu.h, that header also defines some of the options; I have used DEVICE_MEMORY successfully to share memory between a cpu & a dma, but there are many other options and what you need depending upon what bus the dma is connected to.

This code is cribbed with minor variations from the Zynq network interface port for FreeRTOS+TCP:
#define UNCACHED_MEMORY_SIZE 0x100000ul
static uint8_t uncached_mem[UNCACHED_MEMORY_SIZE] attribute ((aligned (UNCACHED_MEMORY_SIZE)));
Xil_SetTlbAttributes( ( uint32_t )(&uncached_mem[0]), DEVICE_MEMORY);

Thanks, Walter.

I’ll have to try this.