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 SCI TX chaining, is this even possible?

Other Parts Discussed in Thread: HALCOGEN

Hello again,

DMA chaining does not look to work at all with SCI TX, simple one shot DMA works without problems. I would like send data from ring buffer to avoid unncessary data copying & wasting space for TX slots and the data should be also sent as a continuous stream so first sending the end of buffer -- waiting for BTC interrupt and then continuing from beginning of the buffer is not an option.

I am out of ideas, except making separate transmit buffer where the whole data is copied and use basic one shot DMA, is this the only option???

According to techical manual the other DMA channel should be started after all frames are sent. In my understanding you have to map SCI TX DMA requests to both of the DMA channels but only start the first one manually with HW trigger (if for some reason both channels would be active at same time, both would try to put data to SCI TX which of course won't work).

Questions (see register status in the end):

- why DMA_CH1 is pending even it has sent all bytes?
- why DMA_CH2 is not triggered (no bytes to console) even though SCI driver is ready and CH2 is pending

PEND register has quite interesting text:

"The pending bit is automatically cleared for the following conditions:
 • At the end of a frame or a block transfer depending on how the channel is triggered as programmed in the TTYPE bit field of CHCTRL"

- Does this really mean that you can sent to peripheral only 1 byte after the chaining (well, where is even that byte then?), because you cannot use block type transfer to peripheral and I also assume that you cannot simultaneously active more than 1 channel to TX.


I have also tried various other methods/configurations to trigger these 2 channels but no luck, and this method (see code below) looks to be rational and correct one.

Below is an example which shows that basic TX works, but if trying to chain, the previously worked channel does not work any more. Same results is received if code from the interrupt is moved to vSciDmaStartSending(), it prints only "wrap".


#define DMA_CH_SCI_TX                        DMA_CH2
#define DMA_CH_SCI_TX_CHAINING  DMA_CH1

INIT:

{

g_dmaCTRL tSciTxCTRLPKT;

    /* Enable DMA */
    dmaEnable();

    /* Enable Interrupt after sending all data */
    dmaEnableInterrupt(DMA_CH_SCI_TX, BTC);

    /* Assigning dma requests for SCI, Refer Data sheet - Default DMA Request Map section */
    dmaReqAssign( DMA_CH_SCI_TX,29U ); /* SCI2/LIN TX */
    dmaReqAssign( DMA_CH_SCI_TX_CHAINING,29U ); /* SCI2/LIN TX */

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

    /* setting dma control packet for TX */
    dmaSetCtrlPacket(DMA_CH_SCI_TX, tSciTxCTRLPKT); /* Now basic settings are ready */
    tSciTxCTRLPKT.CHCTRL = DMA_CH_SCI_TX + 1U; /* Triggers this channel (ring buffer usage) */
    dmaSetCtrlPacket(DMA_CH_SCI_TX_CHAINING, tSciTxCTRLPKT); /* Now basic settings are ready */

    dmaSetPriority( DMA_CH_SCI_TX, HIGHPRIORITY ); /* default is LOW PRIORITY */
    dmaSetPriority( DMA_CH_SCI_TX_CHAINING, HIGHPRIORITY ); /* default is LOW PRIORITY */

     char* pszString = "Hello testing!\r\n";
     vSciDmaStartSending((uint8*)pszString, strlen(pszString) );
}

USAGE:
static void vSciDmaStartSending( uint8* pu8Buffer, uint16 u16Len )
{

             /* Updated required dma control packet information for TX */
            dmaRAMREG->PCP[DMA_CH_SCI_TX].ISADDR  =  (uint32)pu8Buffer;         /* source address       */
            dmaRAMREG->PCP[DMA_CH_SCI_TX].ITCOUNT = SET_LOWORD_U32(dmaRAMREG->PCP[DMA_CH_SCI_TX].ITCOUNT) |
                                                    SET_HIWORD_U32( u16Len );   /* frame count                */

            dmaSetChEnable(DMA_CH_SCI_TX, DMA_HW); /* Enable DMA channel */

}

void dmaGroupANotification(dmaInterrupt_t inttype, uint32 channel)
/* Remarks: ISR: this function is called by HALCoGen code */
{
    if( inttype == BTC )
    {
        if( channel == DMA_CH_SCI_TX )
        {
            char* pszString = "Wrap";
            char* pszString2 = "End!\r\n";
            /* Updated required dma control packet information for TX */
            dmaRAMREG->PCP[DMA_CH_SCI_TX_CHAINING].ISADDR  = (uint32)pszString;        /* source address       */
            dmaRAMREG->PCP[DMA_CH_SCI_TX_CHAINING].ITCOUNT = SET_LOWORD_U32(dmaRAMREG->PCP[DMA_CH_SCI_TX].ITCOUNT) |
                                                             SET_HIWORD_U32( strlen(pszString) );  /* frame count                */

            /* Updated required dma control packet information for TX */
            dmaRAMREG->PCP[DMA_CH_SCI_TX].ISADDR  = (uint32)pszString2;            /* source address       */
            dmaRAMREG->PCP[DMA_CH_SCI_TX].ITCOUNT = SET_LOWORD_U32(dmaRAMREG->PCP[DMA_CH_SCI_TX].ITCOUNT) |
                                                    SET_HIWORD_U32( strlen(pszString2) );   /* frame count                */

            dmaSetChEnable(DMA_CH_SCI_TX_CHAINING, DMA_HW); /* Enable DMA channel - starts other channel after this is ready */

            //dmaREG->HWCHENAS = DMA_CH_SCI_TX_CHAINING | DMA_CH_SCI_TX;

        }
    }
}

RECEIVED OUTPUT:
Hello testing!<CR><LF>
Wrap

Register content after pausing the program execution when no more characters comes out:
Dma_GlbCtrl
00010300
Lin1_SetInt (TX_DMA bit request is active (I have set also rx bits elsewhere)
00070000
Lin1_Flr (TX RDY & TX EMPTY bits active)
00000900

Dma_ChnPnd (CH1 & CH2 is pending (why1 CH1), it has sent everything ("wrap")
00000006    00000505

from 0xFFF80000 (why CCS studio memory browser does not show these as a "registers"?:
00000000    00000000    00000000
00000000    00000000    00000000
00000000    00000000    0000B2B4
FFF7E438    00040001    00000000  //length IFTCOUNT is ok "wrap"
00030008    00000000    00000000 // chain to 3-1=2 channel
00000000    0000B2BC    FFF7E438 //length IFTCOUNT is ok "End!\r\n"
00060001    00000000    00000008
00000000    00000000    00000000
00000000    00000000    00000000
00000000    00000000    00000000
00000000    00000000    00000000
00000000    00000000    00000000
00000000    00000000    00000000

from =0xFFF80800: all zeroes

I have also tried by activating both channels (which must be wrong, because then peripheral makes request to both)

- disabled chaining
    /* setting dma control packet for TX */
    dmaSetCtrlPacket(DMA_CH_SCI_TX, tSciTxCTRLPKT); /* Now basic settings are ready */
    //tSciTxCTRLPKT.CHCTRL = DMA_CH_SCI_TX + 1U; /* Triggers this channel (ring buffer usage) */
    dmaSetCtrlPacket(DMA_CH_SCI_TX_CHAINING, tSciTxCTRLPKT); /* Now basic settings are ready */
- replaced single channel start by starting them both

dmaREG->HWCHENAS = BIT_n(DMA_CH_SCI_TX_CHAINING) | BIT_n(DMA_CH_SCI_TX);
 //dmaSetChEnable(DMA_CH_SCI_TX_CHAINING, DMA_HW); /* Enable DMA channel - starts other channel after this is ready */

This results to print which is expected as now both channels tries to put data simultaneously to TX and this also does not leave any PEND bits actifve as expected
Wnd!<CR><LF>

  • If I understand correctly, you have a circular buffer of data you want to transmit in a continuous stream. This "string" may wrap from the end of the buffer back to the beginning. You use a DMA channel to transfer the first half of the string (mid buffer to end of buffer) then want to chain to a second DMA channel to transfer the second half of the string (start of buffer to end of string). Chaining directly from one channel to the next does not work because when the first channel finishes a block it sends a trigger to the next channel. That channel will send one character to the SCI (which will be lost because the SCI buffer is still full) and then not get any more triggers. The one thing you can try (sorry, I don't have time to try this myself) is to use 3 DMA channels. Setup one channel to transfer the first part of the string. Setup a second channel to transfer the second part of the string but do not enable this channel in the HWCHENAS register. Setup the third channel to copy from Flash to the HWCHENAS register a value that enables the second channel. Have the first channel chain to the third channel. The downside is that the third channel must write a minimum of 8 bits, so the enable status of the 8 channels represented by those bits must be known. (No ability to read-modify-write and only set one bit.)
  • Yes, you understood it correct. So it looks like that my assumption after trial and error was correct that chaining really sends only 1 frame (and it looks to be "SW" style trigger like even though that is not explicitly said anywhere).

    I'll think that your 3rd channel suggestion would work (but also not going to try it because too much time has been already spent for this very simple task) and by the way setting the HWCHENAS should be downside free. 

    HWCHENAS: there must be again a bug in the technical reference manual or actually critical information is completely missing. That register cannot really work in any other way than ignoring 0 writes because individual DMA channel might complete itself at any given time so it would be impossible to keep track of other channels when trying to start one. Also HALCoGen code just sets 1's to that register with basic '=' operation and my receive channel has not been stopped whike doing that. There is also "opposite register" HWCHENAR where description is ok saying 0's are ignored.

    So you will need 3 DMA channels to send data and 2 channels to receive from 1 peripheral :)? One might say that have seen much better DMA implementations. I'll guess that I'll stay in traditional "organize the data so that it isn't wrapped before sending method" and save those 2 DMA channels from TX for some "better future usage".



  • Ok, I will close this thread.