So I've been stuck at the same code for a whole month now, and I'm wondering what's wrong with it.

I'm trying to copy uint32_t from userland. It does not seem to work, I get value of zero and not expected 15.

I put output at the end of the question

#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <stdint.h> 
#include <string.h> 
#include "mem/hmm.h"
#define PAGE_SIZE 4096 
#define DMA_BASE 0x3F007000

#define DMACH0   0x3F007000
#define DMACH1   0x3F007100
#define DMACH2   0x3F007200
#define DMACH3   0x3F007300
#define DMACH(n) (DMACH0 + (n)*0x100)

#define DMAENABLE 0x3F007ff0
#define DMAINTREG 0x3F007fe0

#define DMA_CS_RESET (1<<31)
#define DMA_CS_ACTIVE (1<<0)
#define DMA_ENABLEBIT (1<<5)
#define DMA_BUS_ADDRESS 0xC0000000
#define DMA_DEBUG_READ_ERROR (1<<2)
#define DMA_DEBUG_FIFO_ERROR (1<<1)
#define DMA_NO_WIDE_BURSTS (1<<26)
#define DMA_CB_TI_DEST_INC (1<<4)
#define DMA_CB_TI_SRC_INC (1<<8)

struct DmaChannelHeader {
    uint32_t CS; 
    uint32_t CONBLK_AD; 
    uint32_t TI; 
    uint32_t SOURCE_AD; 
    uint32_t DEST_AD; 
    uint32_t TXFR_LEN; 
    uint32_t STRIDE; 
    uint32_t NEXTCONBK; 
    uint32_t DEBUG; 

struct DmaControlBlock {
    uint32_t TI; uint32_t SOURCE_AD; 
    uint32_t DEST_AD;
    uint32_t TXFR_LEN; 
    uint32_t STRIDE; 
    uint32_t NEXTCONBK; 
    uint32_t _reserved[2];
}__attribute__ ((aligned(32)));

void rpi_main() {
char buffer[33];

uint32_t *physDestPage;
uint32_t *physSourcePage;
uint32_t *intreg;
intreg  = DMAINTREG;
*intreg = 0;
uint32_t *enablebits = (uint32_t*)DMAENABLE;
*enablebits = DMA_ENABLEBIT;
int dmaChNum = 5;

volatile struct DmaChannelHeader *dmaHeader = (volatile struct DmaChannelHeader*)(DMACH(dmaChNum));

    buffer[33] = 0;
    itoa (dmaHeader->CS,buffer,2);
    printf ("dmaHeader->CS: %s\n",buffer);

dmaHeader->CS = DMA_CS_RESET;
physSourcePage = *handles[0];
physDestPage = *handles[2];

volatile struct DmaControlBlock *cb1 = (volatile struct DmaControlBlock*)(*handles[1]);

    //fill the control block:
    cb1->TI = 0;
    cb1->TI = DMA_CB_TI_SRC_INC;
    cb1->TI = DMA_CB_TI_DEST_INC;
    cb1->SOURCE_AD = (uint32_t)(&physSourcePage) | DMA_BUS_ADDRESS;
    cb1->DEST_AD = (uint32_t)(&physDestPage) | DMA_BUS_ADDRESS;
    cb1->TXFR_LEN = 100; 
    cb1->STRIDE = 0; 
    cb1->NEXTCONBK = 0; 
    cb1->_reserved[0] = 0;
    cb1->_reserved[1] = 0;

    buffer[33] = 0;
    itoa (dmaHeader->DEBUG,buffer,2);
    printf ("dmaHeader->DEBUG: %s\n",buffer);

    dmaHeader->CS |= 1<<29;
    dmaHeader->CONBLK_AD = (uint32_t)(&cb1) | DMA_BUS_ADDRESS; 
    dmaHeader->CS |= 1;
int i;
    for (i = 0;i < 5000; i++)
    buffer[33] = 0;
    itoa (dmaHeader->CS,buffer,2);
    printf ("dmaHeader->CS: %s\n",buffer);

    buffer[33] = 0;
    itoa (*physSourcePage,buffer,2);
    printf ("*physSourcePage: %s\n",buffer);

while(*physDestPage == 0)


Output : dmaHeader->CS: 1000 dmaHeader->DEBUG: 100000000011000010100000000 dmaHeader->CS: 100000000000000000000000000001 *physSourcePage: 1111 *physDestPage : 0 (value has not changed since initialization)

P.S. Sorry for some lines of code which are left overs, I've been testing a lot.

  • Please edit your question and include your code, rather than using a third party service. This prevents link rot and makes your question self contained. – Steve Robillard Aug 23 '16 at 16:13
  • I'm really sorry, I can't seem to understand how to use edit the script into the text like in this question : raspberrypi.stackexchange.com/questions/47408/… – IamU Aug 23 '16 at 16:31
  • Click the edit button. On a blank line paste your code, and highlight it. Then click the code icon "()" in the editor toolbar. Also had you posted your actual code instead of asking for information about DMA you might have already had an answer. – Steve Robillard Aug 23 '16 at 16:44
  • Thanks. You're right, but before I move on, I wanted to know as much as possible about DMA controller. – IamU Aug 23 '16 at 16:49

I'm going to guess you are trying to do this from Linux userland.

You need to pass the DMA hardware the bus address of the memory you want to transfer. These are physical addresses.

You have to remember that the Pi has multiple MMU (Memory Management Unit).

The memory used by a Linux userland process is logical memory. You have no control of where that logical memory is in the physical address space.

You need to map the logical memory address to a physical address. You do that by opening the page map file for your user process and finding the corresponding entry for the memory you are interested in. That entry tells you the physical address. You then need to map that physical address to a bus address. On single core Pis you could do that by adding 0x40000000 to the physical address.

You also need to find the address of the DMA hardware. You do that by using the mmap function.

This isn't trivial. Have a look at servoblaster useland variant code which is probably the simplest complete example you will find.

I suggest you now use the mailbox interface to grab DMA memory. It's a simpler process than the above, but will only work on the Pi.

Again have a look at servoblaster or my pigpio.c code.

  • I'm sorry, maybe you got that wrong but, I'm not using Linux, it's bare metal. However, my first example of DMA controller code was code in Linux. github.com/Wallacoloo/Raspberry-Pi-DMA-Example/blob/master/… to be exact – IamU Aug 23 '16 at 18:54
  • I don't use bare metal so can't help. I suggest you ask the question on the bare metal forum at raspberrypi.org. If you do, make sure that your example compiles (the posted example doesn't because #include "mem/hmm.h" is a local include). – joan Aug 23 '16 at 19:35

I believe the problem is that you're overwriting your control block info multiple times instead of putting the flags you want together.

The lines

cb1->TI = 0;

should be combined into


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.