DMA Error

27 Jan 2015

I am trying to set up DMA with I2S in a FRDM K22F. I got I2S running and the module it writing data to the I2S0_RDRO register. I have it set up to request DMA on FIFO Request Flag. I am now having a very hard time with DMA. My code is below:

    SIM_SCGC6 |= 0x2;                         //DMAMUX Module Turn On  
    SIM_SCGC7 |= 0x2;                         //DMA Module Turn On  
    DMAMUX_CHCFG0 = 0x8C;           //I2S Source: Slot 12 & Activate      
      
    DMA_TCD0_CSR &= 0xBF;             //Inactive   
    NVIC_EnableIRQ(DMA0_IRQn);      //Enable IRQ DMA Channel 0  
          
    DMA_TCD0_SADDR = (uint32_t)&I2S0_RDR0;    //Data Source   
    DMA_TCD0_DADDR = (uint32_t) BUFFER_A;     //Destination  
      
    DMA_TCD0_SOFF = 0;                       //No Source Offset  
    DMA_TCD0_SLAST = 0;                     //Nothing Added to Source Address after Major Loop   
    DMA_TCD0_DLASTSGA = -4096;      //Value Added to Destination Address after Major Loop  
    DMA_TCD0_DOFF = 4;                       //4 Byte Destination Offset   
    DMA_TCD0_NBYTES_MLNO = 4;       //4096 Bytes Transfered in Minor Loop  
    DMA_TCD0_BITER_ELINKNO = 1024;  //1024 Bins     
    DMA_TCD0_CITER_ELINKNO = 1024;  //1024 Bins  
    DMA_TCD0_ATTR = 0x202;                  //32-Bit Transfer Size  
    DMA_TCD0_CSR = 0x2;                       //Interrupt Major Iteration  
      
    DMA_SERQ = 0x0;                               //Channel 0 Enable   
    DMA_TCD0_CSR |= 0x41;                   //Enable! 

I have a while (1) loop in main which just reads to DADDR register to make sure the destination address of my BUFFER_A is updating correctly. But it isn't and my code is crashing:

40009010 --> 1fff0234 
40009010 --> 1fff04dc 
40009010 --> 1fff0788 
40009010 --> 1fff0a38 
40009010 --> 1fff0ce4
...code crashes 
27 Jan 2015

To start with, what makes you say your destination address isn't updating properly? It looks correct to me.

27 Jan 2015

I guess you are right, I think I was expecting to see the address to update by 4 each time like so:

Address of BUFFER_A[0] = 0x1fff0230
Address of BUFFER_A[1] = 0x1fff0234
Address of BUFFER_A[2] = 0x1fff0238

Maybe the while loop runs slower than the DMA. I don't know why my code crashes then, DMA_ES is not flagging any errors. DMA_TCD0_CITER_ELINKNO is correctly decreasing from 1024 to 203 before it crashes...

It seems my DMA config is not loading data in BUFFER_A. Here is a printf of CINT followed by BUFFER_A[i]

40009016 --> 1024 
0.000000
40009016 --> 810 
nan
40009016 --> 639 
nan
40009016 --> 467 
nan
40009016 --> 296 
nan
40009016 --> 125      ... CRASHES

27 Jan 2015

I assume you run your serial at significantly above 9600? Otherwise it will probably only print a single line before being finished :P. I2S will run at around 48kHz per 8 bytes. 9600 baud is 1kHz per character: you can write a ton of I2S messages for every serial line.

What happens if you disable your interrupt?

27 Jan 2015

Baud! That would make perfect sense!

If I turn off the NVIC interrupt my code doesn't crash. I also printed the I2S0_RCSR register and it seems like the FRF flag never goes on. When I turn off DMA and test I2S by itself it does flag FRF.

Here's a piece from the serial printing CINT and BUFFER_A

4002f080 --> 90100001    (I2S0_RCSR)
40009016 --> 32               (CITER)
0.000000                            (BUFFER_A[i])
4002f080 --> 90100001 
40009016 --> 1 
0.000000
4002f080 --> 90100001 
40009016 --> 995 
0.000000

27 Jan 2015

I also occasionally get nan's from BUFFER_A

27 Jan 2015

Well I don;t know what you are supplying it with. But regarding the crashing, the obvious question is that is your interrupt code doing? Can you paste your interrupt routine here?

27 Jan 2015

Right now I am just trying to light an LED and it still crashes, but here's my routine:

void RX_CALL(){
   
   float volatile *DMA_BUFF;
   float volatile *YOUR_BUFF;
   
    if(USING_BUFFER_A){
       USING_BUFFER_A = 0;
       DMA_BUFF = BUFFER_B;
       YOUR_BUFF = BUFFER_A;
    }
    else{
        USING_BUFFER_A = 1;
        DMA_BUFF = BUFFER_A;
        YOUR_BUFF = BUFFER_B;     
    }
}

//DMA 0 Interrupt Handler
void DMA0_IRQHandler(){
    led_red = 1;
    //RX_CALL();
    //DMA_TCD0_DADDR = (uint32_t)&BUFFER_A;
    //DMA_CINT |= 0x40;  
    return; 
}

27 Jan 2015

That code is supposed to crash ;).

In general I prefer sending a message of serial terminal: If you do that you probably would see many messages, nothing is disabling the interrupt! I guess normally the CINT setting should do that? But for sure let it for example print to your terminal, then you see if it enters the interrupt properly, and how often.

27 Jan 2015

Ok, that makes sense. I have updates the code to printf and CINT should clear the interrupt. Still the code crashes... I wonder whether CINT clears the NVIC interrupt or another?

//DMA 0 Interrupt Handler
void DMA0_IRQHandler(){
    DMA_CINT = 0x0;
    pc.printf("something\r\n");
    //RX_CALL();
    //DMA_TCD0_DADDR = (uint32_t)BUFFER_A;  
    return; 
}

27 Jan 2015

Here's all of my code for reference. I am using a k22f board.

    CHIPEN = 1;
    R = 0;

    //Pins Init
    SIM_SCGC5  |= 0x42B82;      //Enables PORTC
    PORTC_PCR5 |= 0x400;        //PTC5 --> I2S0_RXD0 -> DATA
    PORTC_PCR7 |= 0x400;        //PTC7 --> I2S0_RX_FS -> FRAME
    PORTC_PCR9 |= 0x400;        //PTC9 --> I2S0_RC_BCLK -> BITCLOCK

    //Clock Init
    SIM_SCGC6 |= 0x8000;        //Turn on I2S Clock Gate
    SIM_SOPT2 |= 0x30000;       //I2S Clocking Sourced from 48MHz IRC48MCLK
    I2S0_MCR  |= 0x43000000;    //Output Enable. Mux IRC48MCLK into Fractional Divider
    I2S0_RCR2 |= 0x4000000;     //MCLK MSEL to BCLK
    I2S0_MDR  |= 0x1F07C;       //Set I2S Master Clock to 12.2880MHz
    I2S0_RCR2 |= 0x300000D;     //DIV is 13 - > 9000kHz Sample Rate. BCD = 1
    SIM_SCGC6 |= 0x8000;        //Re-Enable I2S Module

    //Receive Init
    I2S0_RCSR  = 0x0;           //Disable Receive as per Datasheet
    I2S0_RCR1 |= 0x1;           //Sets FIFO Watermark to 1
    I2S0_RCR3 |= 0x10000;       //Enable Receive Data Channel
    I2S0_RCR4 |= 0x10011F1B;    //Internal FS. Early FS. 32 SYWD. FRSZ 2. FCONT
    I2S0_RCR5 |= 0x1F1F1F00;    //32 Bits per Word. 32 Bits in First Word. Shifted 32.
    I2S0_RMR  |= 0x2;           //Mask 2nd Word (One Channel Only)
    
    I2S0_RCSR |= 0x80000001;    //Enable & Request DMA   
}

//DMA
void START_DMA(){
    
    SIM_SCGC6 |= 0x2;               //DMAMUX Module Turn On
    SIM_SCGC7 |= 0x2;               //DMA Module Turn On
    DMAMUX_CHCFG0 = 0x8C;           //I2S Source: Slot 12 & Activate    
    
    NVIC_EnableIRQ(DMA0_IRQn);      //Enable IRQ DMA Channel 0 
    DMA_TCD0_CSR &= 0xBF;           //Inactive 
    DMA_CR = 0x0;                   //Enable Minor Looping   
        
    DMA_TCD0_SADDR = (uint32_t)&I2S0_RDR0;    //Data Source 
    DMA_TCD0_DADDR = (uint32_t) BUFFER_A;     //Destination
    
    DMA_TCD0_SOFF = 0;              //No Source Offset
    DMA_TCD0_SLAST = 0;             //Nothing Added to Source Address after Major Loop 
    DMA_TCD0_DLASTSGA = (int)-4092; //Value Added to Destination Address after Major Loop
    DMA_TCD0_DOFF = 4;              //4 Byte Destination Offset 
    DMA_TCD0_NBYTES_MLNO = 4;       //4096 Bytes Transfered in Minor Loop
    DMA_TCD0_BITER_ELINKNO = 1024;  //1024 Bins   
    DMA_TCD0_CITER_ELINKNO = 1024;  //1024 Bins
    DMA_TCD0_ATTR = 0x202;          //32-Bit Transfer Size
    //DMA_TCD0_CSR = 0x2;             //Interrupt Major Iteration
    
    DMA_SERQ = 0x0;                 //Channel 0 Enable
    DMA_TCD0_CSR |= 0x41;           //Enable!
}

//RX Interrupt Callback
void RX_CALL(){
   
   float volatile *DMA_BUFF;
   float volatile *YOUR_BUFF;
   
    if(USING_BUFFER_A){
       USING_BUFFER_A = 0;
       DMA_BUFF = BUFFER_B;
       YOUR_BUFF = BUFFER_A;
    }
    else{
        USING_BUFFER_A = 1;
        DMA_BUFF = BUFFER_A;
        YOUR_BUFF = BUFFER_B;     
    }
}

//DMA 0 Interrupt Handler
void DMA0_IRQHandler(){
    DMA_CINT = 0x0;
    pc.printf("something\r\n");
    //RX_CALL();
    //DMA_TCD0_DADDR = (uint32_t)BUFFER_A;  
    return; 
}
27 Jan 2015

So how often does your code do the printf?

27 Jan 2015

Baud is set at (115200). Right now I am trying to just read from an array. The problem persist so I don't believe I2S is involved here.

I basically uncomment the NVIC enable line and the code crashes, even with the CINT clear interrupt.

27 Jan 2015

I think the issue has to do with the FRF flag never going on, so the DMA interrupt never happens or something like that. Weird thing is, the flag does work when isolated from DMA.

I wonder if some macro or something having to d0 with the DMA0 ISQR Handler is wrong.

27 Jan 2015

So the interrupt never fires? I might have a slight idea :P.

Set the interrupt using a dynamic vector (Nvic_SetVector(....)), to the function with a different name (this one is reserved). There is a double bug with the K22F interrupt vector: Firstly it is relocated to the wrong location (in the middle of the RAM, so if you use near 50% of your RAM, it will crash. I reported the bug, still open). Secondly is that it only relocated part of the interrupt vectors. I am not sure, so maybe it isn't the problem, but if the DMA is one of those it does not relocate, it will crash.

If you set it dynamically it does not need to relocate anything. The first bug is still relevant then, but you arent using that much ram yet (when you end up at 0x1FFFE000 you have a problem), and the second one should be circumvented.

27 Jan 2015

Ok, so while looking up in Google for "NVIC_SetVector" because I don't know what it is... :). I stumbled across another post, apparently you have to declare the handler as extern "C" void

extern "C" void DMA0_IRQHandler(){
    pc.printf("something\r\n");
    DMA_CINT = 0;
    //RX_CALL();
    //DMA_TCD0_DADDR = (uint32_t)BUFFER_A;  
    return; 
}

The post is here: http://developer.mbed.org/forum/mbed/topic/419/?page=1#comment-2126

This is crazy, how is one supposed to know this? I have been stuck for now two days...

It works now. Thank you very much!

27 Jan 2015

Ah extern c, oops, missed that :P.

Easiest way to know it: Don't use it. Use dynamic interrupt vectors, so just setvector to whatever function you want it to go. And those are irritating issues, but in general the way to figure them out is to try to step by step see where it is going wrong.