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.

DMA chaining, does it work at all?

Other Parts Discussed in Thread: RM46L852, HALCOGEN

Couldn't make simpler example of DMA chaining. I can't get it working doesn't matter what I try.

"u32ChSw" will be assigned to 0x12345678, but "u32ChChain" stays in 0.

ChnPnd-register has value 0x00000200 indicating that CH9 should be run but it does not start it.

How this chaining should work, because it strongly looks like that whole feature is broken?

#include <sys_dma.h>
#define DMA_TEST_SW_CH        DMA_CH8
#define DMA_TEST_CHAINED_CH     DMA_CH9

uint32 u32DataPattern = 0x12345678;
uint32 u32ChSw = 0U;
uint32 u32ChChain = 0U;

void vDmaChainTest( void )
{
    dmaEnable();

    g_dmaCTRL tCTRLPKT;
        /* Populate dma control packets structure for TX */
    tCTRLPKT.CHCTRL    = 0;                 /* channel control            */
    tCTRLPKT.ELCNT     = 1;                 /* element count              */
    tCTRLPKT.ELDOFFSET = 0;                 /* element destination offset */
    tCTRLPKT.ELSOFFSET = 0;                 /* element source offset      */
    tCTRLPKT.FRDOFFSET = 0;                 /* frame destination offset   */
    tCTRLPKT.FRSOFFSET = 0;                 /* frame source offset        */
    tCTRLPKT.PORTASGN  = 4;                 /* port b                     */
    tCTRLPKT.RDSIZE    = ACCESS_32_BIT;      /* read size                  */
    tCTRLPKT.WRSIZE    = ACCESS_32_BIT;      /* write size                 */
    tCTRLPKT.TTYPE     = FRAME_TRANSFER;       /* transfer type              */
    tCTRLPKT.ADDMODERD = ADDR_FIXED;        /* address mode read          */
    tCTRLPKT.ADDMODEWR = ADDR_FIXED;        /* address mode write         */
    tCTRLPKT.AUTOINIT  = AUTOINIT_OFF;      /* autoinit                   */
    /* Following items needs to be set when transferring */
    tCTRLPKT.DADD      = (uint32)&u32ChChain;   /* destination address*/
    tCTRLPKT.SADD      = (uint32)&u32DataPattern;                   /* source address             */
    tCTRLPKT.FRCNT     = 1U;                    /* frame count                */

    dmaSetCtrlPacket(DMA_TEST_CHAINED_CH, tCTRLPKT);

    tCTRLPKT.CHCTRL    =  DMA_TEST_CHAINED_CH+1U;
    tCTRLPKT.DADD      = (uint32)&u32ChSw;   /* destination address*/
    dmaSetCtrlPacket(DMA_TEST_SW_CH , tCTRLPKT);

    dmaSetChEnable(DMA_TEST_SW_CH, DMA_SW); /* Enable DMA channel */

    while( 1 ){};
}


void main(void)
{
    vDmaChainTest();

}

  • Jarkko,

    Chaining works, I've used it before.   Unfortunately I don't have an example to send you. 

    Did you enable your chained channel with type HW?  (It doesn't look like it..)

    -Anthony

  • Hello again,

    Yep, for some very weird reason the "HW_enable" needs to be set in order make the chaining works

    It tested it following way:

    This resulted to nothing copied in any variable (as expected but wanted to test it):

    dmaSetChEnable(DMA_TEST_CHAINED_CH, DMA_HW); /* Enable DMA channel */
    //dmaSetChEnable(DMA_TEST_SW_CH, DMA_SW); /* Enable DMA channel */

    This resulted to copying to both variable:

    dmaSetChEnable(DMA_TEST_CHAINED_CH, DMA_HW); /* Enable DMA channel */
    dmaSetChEnable(DMA_TEST_SW_CH, DMA_SW); /* Enable DMA channel */

    Where this is said that in order to use chaining you must enable HW source (not in TRM (RM46L852) chapter 16.2.13 and not in anywhere else in this document, nor in the data sheet)?

    This is really weird, now when I have to active HW trigger for this channel, this channel is automatically armed also for any configured "DMA request line connection" (DREQASIx), see table 6-33 from RM46L852 data sheet. So there is no way to prevent external DMA requests for given channel because 1 source is always configured for one DMA channel (DREQASIx). So you will need to find a DMA trigger source which you are not using in our application (hopefully you find one:)) and set that source for your chained DMA channel in order to guarantee that channel is not triggered "accidentally".

    What this text actually means in TRM:

    PEND register: 1 == "The corresponding channel is pending and is waiting for service."

    For me this sounds that the corresponding channel goes active asap when DMA is free from higher priority channels, but obviously this this does not do it.

    HWCHENAS: "1 == The corresponding channel is enabled for hardware triggering"
    For me this sounds table 6-33 sources from data sheet, not chaining...

  • I made "interesting" observation, whole DMA looks to be completely useless if you should for example to sent data from ring buffer to peripheral without cpu intervention, that looks to impossible even though this should very basic operation.

    Lets modify the simplified example a bit an use 16 bit data with a different value:
    uint16 u16DataPattern = 0x0001; // this line
    uint32 u32ChSw = 0U;
    uint16 u16ChChain = 0U; // this line

    So modify data lengths:
    tCTRLPKT.RDSIZE = ACCESS_16_BIT; /* read size */
    tCTRLPKT.WRSIZE = ACCESS_16_BIT; /* write size */

    And modify variable names
    tCTRLPKT.DADD = (uint32)&u16ChChain; /*(uint32)(&(dmaREG->HWCHENAS)); */ /* destination address*/
    tCTRLPKT.SADD = (uint32)&u16DataPattern; /* source address */

    And then start:
    dmaSetChEnable(DMA_TEST_CHAINED_CH, DMA_HW); /* Enable DMA channel */
    dmaSetChEnable(DMA_TEST_SW_CH, DMA_SW); /* Enable DMA channel */

    --> works as expected, value 1 is in u16ChChain, ok to proceed.

    Lets modify a bit further, lets put destination address into DMA memory are (so that we could start another channel).
    tCTRLPKT.DADD = (uint32)(&(dmaREG->HWCHENAS));

    This is possible because TRM chapter 16.2.1 states that any 4 gigabyte address is ok? This method was also suggested in this thread (e2e.ti.com/.../519893).

    --> HWCHENAS has value 0, so chaining does not work...

    Checking the memory that the chained DMA channel is configured correct way (yep, 0xFFFFF014 is HWCHENAS register)

    Memory from 0xFFF80118:
    00000000 00000000 08002676 FFFFF014 00010001

    Lets try some some other DMA register just in case the enabling of HW is illegal for uninitialized channel (my real life code has initialized it) (channel 0 initial count, should be in memomry 0xFFF80008 right?)
    tCTRLPKT.DADD = (uint32)&dmaRAMREG->PCP[0].ITCOUNT

    --> 0xFFF80008 has value 0, so chaining does not work

    Memory from 0xFFF80000:
    00000000 00000000 00000000 00000000 00000000


    In any cases PEND register has value 0, so the chained channel has been apparently run but it just doesn't do anything if destination is in peripheral memory area...

    Also DMA interrupt register shows that both channels has been run because there are 2 pending BTC interrupts
    Dma_BTCIntFlg:
    00000300


    I really would like to see example from Texas where you have an array
    char Array[] = { 'l', 'o', 'h', 'e', 'l' }
    and you output that into for example SCI without any CPU intervention so that end result is "hello", I haven't manage to do that...

    I have managed to get this working only by sending first the end of array and in BTC interrupt check if there is data in the beginning of the array and continue from that.
  • Jarkko,

    I don't think useless is really a fair statement -- the DMA has been in use for many years now across different generations of this MCU family.

    But the manual is large, can be difficult to understand sometimes, and there is no GUI to help you with DMA like there is for HalCoGen on the other peripherals.   So I can understand the learning curve can feel frustrating.     If you have specific points you would like to see clarified in the TRM, there is a button on the bottom of each TRM page that will allow you to submit this feedback directly to TI through our technical documentation dept.   If  you do this when you find something confusing it will definitely help us improve the documentation.   

    There is a figure in the TRM that illustrates that chaining is in parallel to the HW DMA request and that it sets the pending flag.

    So if the channel is not enabled for hardware DMA requests then the chaining path is broken.

    The DMA can support ping/pong buffers with the 'HBC' interrupt in addition to the 'BTC' interrupt.

    The 'ring' buffer is the size of the entire block and you configure the DMA control packet to reinitialize itself (so it will operate continually)

    You'll get an interrupt at the half block and full block boundaries.

    I'm not really sure what you are alluding to with the address range.   The DMA isn't aware of the memory map of the device, so yes you can put in any address legal or illegal.  

    It's the same in C code, nothing prevents you from setting a pointer to an unimplemented portion of the 4G address space and dereferencing the pointer.  If you do this though you will likely get a data abort - especially if you configure the MPU.

    Likewise the DMA has it's own MPU so that you can restrict which regions of memory you can access via the DMA.   You should consider using this feature of the DMA controller to protect your system from corruption of memory if the DMA controller behaves badly (say a bit flips due to soft error..)


    The DMA cannot program it's own registers as described in the TRM because it does not have 'Privilege' to do so:

    In addition - any code that you write that programs the DMA musts be executed in a privileged mode  [basically any mode that isn't USER mode, but likely it will be SYSTEM or SVC].

    This is another protection for the integrity of the system - it restricts the reprogramming of the DMA to limit the damage that runaway code or buggy code can do.

    The downside is you cannot be so creative in setting up complex chains of DMA events -- but every design has trade-offs and protecting operation is given a lot of priority in Hercules devices.

  • Thanks for reply with details,

    Of course the DMA is not completely useless, but as I said, practically useless to make any neat solutions. So you cannot do following basic operations (by chaining) with DMA:
    - use ring buffers to TX data without CPU intervention (if continuous stream is needed to output then you need to organize data so that it is 1 array, or if stream can break, use BTC interrupt t to start from beginning of array)

    Also starting other peripherals (without cpu intervention) after doing something with DMA is really hard if not impossible, you must start the DMA to wait for HW trigger and when initial DMA finishes, kick chained channel to enable peripheral's DMA request(s) (register content cannot change in between, because you can't OR with DMA)...


    "There is a figure in the TRM that illustrates that chaining is in parallel to the HW DMA request and that it sets the pending flag.
    So if the channel is not enabled for hardware DMA requests then the chaining path is broken."
    - figure 16-16 says that in chaining the pending goes active regardless of DMA_REQs, that picture nor anything else still not say that HW trigger needs to be enabled. Of course if using wild imagination with together PEND register description it may be possible solve this puzzle, I also saw one code example where HW triggers were set to chained channels without any comments and wondered the reason (DMA requests left to default in that example, it wasn't commented anyway either). Of course now when you know it, no problems, just remember to map DMA request to something that does not generate requests to your HW trigger...

    Thanks for pointing out chapter 16.2, I completely forgot that even I now recall reading it when first starting to evaluate DMA ~3 weeks ago but happily forgot it at this point.
    - So the "kludge" information received from other TI exployee in other thread a couple of weeks ago was wrong information e2e.ti.com/.../519893

    "I'm not really sure what you are alluding to with the address range"
    - As said above, I forgot that DMA restriction which was said else where (right above :)), I was only looking chapter 16.2.1 and thought that it is ok to modify DMA area. I was completely blind for above rows and/or couldn't connect the dots (maybe because I thought that such a simple procedure is doable in some way (boosted by false information received in another thread), this is first device I encountered which cannot do this).

    Final questions:
    - When I violated the DMA memory usage ("re-program itself"), why BTC interrupt flag went active basically indicating that channel made what was asked to do? Just because it was last frame to transfer, if more frames would have left, then only FTC would have been active (still indicating that something was done)?
    - How you can see that some problem with DMA happened? DMAMPCTRL&DMAMPST looks to give information only for user configured addresses (should I configure DMA memory region here even though those are always restricted?) and there aren't any other registers at least in DMA section. Is somewhere else some registers / configurable interrupt (couldn't find any feasible interrupt from data sheet)?
  • Jarkko Silvasti said:
    - When I violated the DMA memory usage ("re-program itself"), why BTC interrupt flag went active basically indicating that channel made what was asked to do? Just because it was last frame to transfer, if more frames would have left, then only FTC would have been active (still indicating that something was done)?

    Jarkko,

    So first I should tell you that the statement about the DMA is a bit broader than 'DMA cannot program itself'.
    DMA cannot program any register on the chip, in any peripheral, which has the '/WP' attribute. 

    We put this attribute '/WP' .. normally part of 'R/WP' on bits that require privilege to write.  The DMA acts like a CPU in user mode.

    This type of protection is mainly on the register and the register typically just ignores the write.  If you use the CPU to do the write you usually get a data abort but the DMA has no such exception.

    The only way to really protect the DMA is to program it's memory protection unit to limit the areas of memory which it can access.   If you program this and enable it then you can get an interrupt on a violation.

    The BTC completes because the DMA issues the writes and completes... but because they are issued in user mode (without the "P" status on the bus) the registers in the DMA ignore the write.    They're sent, but not received if you will.  That's why you get BTC.


    -Anthony