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.

C6742 / C6746 / C6748 UART and EDMA considerations

Hello!

Spec of the system:

  • 50us ISR with timecritical stuff
  • Serial connection with 9.6K to 1M Baud
  • Serial protocol that does not transmit a fixed framelength at a time but is variable

Thoughts:

  • Since the protocol does not have a fixed framelength, i'd need to setup the RX Fifo trigger level RXFIFTL in FCR to 0 to get an event upon each byte recieved?
  • Serial communication should be serviced at some lower priority task, maybe every 1ms because there's other, more important stuff to do for the CPU
  • The preferred setup would be "Let EDMA transfer bytes from fifo to buffer untill the tasks services the UART"

Questions:

  • could "Let EDMA transfer bytes from fifo to buffer untill the tasks services the UART" be implemented anyways?
  • If not, is there a way to achieve high baud rates (1MBaud) with EDMA transfers whith servicing UART at a low interval (1ms)
  • Can i setup the edma to transfer n-bytes from UART RX FIFO to a buffer and stop the EDMA transfer within the UART service routine?

Thank you for your help!

  • 11,

    I will offer some ideas, but they might not be perfect. You will need to look in detail in the datasheet and TRM or UART User Guide to confirm and to see how to program things. You will see from some of my comments and questions that I do not know the details in the UART peripheral, but maybe this will be helpful anyway.

    This all might be solved in the UART User Guide, so be sure to look there to see if a good solution is provided. In that case, you can ignore everything below.

    1768011 said:
    Since the protocol does not have a fixed framelength, i'd need to setup the RX Fifo trigger level RXFIFTL in FCR to 0 to get an event upon each byte recieved?

    If you want an EDMA event on every byte, then yes. That is not a good use of the capabilities of the UART, but it might end up that way if you cannot get things working another way. But we should look at other ways first.

    1768011 said:
    Serial communication should be serviced at some lower priority task, maybe every 1ms because there's other, more important stuff to do for the CPU

    My calculator says that at 1MBaud assuming 8-N-1 configuration, you will transfer 1 Byte every 10us. This means you will transfer 100 bytes in 1ms. Is the FIFO deep enough to hold that much, comfortably? I would recommend setting the FIFO threshold to about 50% of the FIFO depth, and also set a timer event to about the time it will take to fill the FIFO to that 50% point. Maybe different points in the FIFO and in time should be used, but your expertise will be useful for making that determination based on your engineering judgement.

    1768011 said:
    could "Let EDMA transfer bytes from fifo to buffer untill the tasks services the UART" be implemented anyways?

    Yes. With any FIFO and Timer settings you choose, the EDMA can be copying in the received data until you are ready to do something with that data.

    1768011 said:
    is there a way to achieve high baud rates (1MBaud) with EDMA transfers whith servicing UART at a low interval (1ms)

    You will always want to give the EDMA as much data to transfer as possible before triggering it to start the transfer. This will be the most efficiency you can get. That means setting the FIFO to some level that will reduce the number of EDMA events required in time.

    1768011 said:
    Can i setup the edma to transfer n-bytes from UART RX FIFO to a buffer and stop the EDMA transfer within the UART service routine?

    When the EDMA is triggered, the number of bytes that it will read must be less than or equal to the number of bytes in the UART FIFO. You cannot tell the EDMA to read 10 bytes when there are only 6 bytes in the FIFO, for example; other wise, you will cause an underrun situation.

    This is a common discussion topic: how to receive an unknown number of bytes coming in from any peripheral. With something like a UART, you will have to be able to respond even when a message of length 7 bytes is sent or when a message of 200 bytes is sent.

    If you can live with a little latency in your response time, the most efficient way I can think of is to use both the FIFO level to have the EDMA automatically service the UART whenever a certain threshold is met and use a timer to go check if a complete message has been received.

    Let's say the timer ISR is triggered once per second. A lot of data could have come in during that time, so you would want the EDMA responding to UART FIFO threshold events as data comes in, if any. Once the timer ISR is started, it will read the available data and check for a complete message. If a complete message is found, then that message can be pulled from the buffer and acted upon. Then check for another message, and another, until there is no complete message remaining in the buffer.

    If no complete message was found at the beginning, it is possible that the end of a message is sitting in the UART FIFO but is not enough data to trigger the EDMA. You should be able to read the UART registers to find out if data is available in the FIFO, and find out how many bytes are available. Then the ISR could disable the EDMA, read that many bytes from the UART into the buffer, consume the message (if complete), then re-enable the EDMA to start responding again with an updated destination address.

    There may be race conditions that need to be considered when disabling the EDMA (clearing the EER bit for the UART DMA channel) and maybe when re-enabling it later. If the FIFO is close to reaching its threshold when the ISR starts all of this, then it might get one more byte coming in at same time that you are trying to stop/start things. This is just something to be careful with.

    I hope this is helpful in some way. If it does not make sense, then I will probably have to clear it out and let someone else come in with better answers. Sorry, but I do hope this gives you some ideas to work with, even if it does not give you a working example.

    Regards,
    RandyP

  • Hello RandyP,


    thank you very mouch for your detailed and helpful response on the issue.

    First i'd like to post some code and issues i've beend facing with the TX channel:

    TX: Sending data via UART+EDMA was not that easy as i thaught:

    • I assumed that i could have a EDMA transfer that copies more than 1 byte per "TX FiFo empty event"
    • From my experiments, i believe this is not possible with A-Synchronized Paramsets
    • I assumed that the OPT field's FWID portion would do some magic here: if A-Cnt is 4, and B-Cnt is 2, only two Bytes (Byte 0 and Byte 4) would be actually transferred out of 8 desired Bytes, even if FWID is set to 0 (8-Bit FiFo width).  Could you please explain the usecase of FWID, which is not clear to me.
    • As a workaround, i've set the A-Cnt to 1 and B-Cnt to the TX-Telegram length and used A-Sync. This is not the most performant approach, because each transfer request could handle upto 16 bytes (FiFo size). I guess more ideal would be to have A-Cnt=1, B-Cnt=16 and C-Cnt=(telegramlength/16) and AB-Sync.
    // L2 writeback on outbuffer
    
    // put UART in reset
    UART->PWREMU_MGMT = 0;
    
    // clear Event via: ECR, SECR, EMCR, IECR, EECR and ICR
    ...
    
    EDMA0->PARAMSET[EVENT_TX].OPT = (EDMA_PARAM_OPT_FWID_8BIT
                                    |EDMA_PARAM_OPT_STATIC_NO
                                    |EDMA_PARAM_OPT_DAM_CONST
                                    |EDMA_PARAM_OPT_SAM_INCR
                                    |EDMA_PARAM_OPT_TCINTEN
                                    |(EVENT_TX << 12));
    EDMA0->PARAMSET[EVENT_TX].SRC     = (uint32)&serOutBuff.buffer[0];
    EDMA0->PARAMSET[EVENT_TX].DST     = (uint32)&UART->THR;
    EDMA0->PARAMSET[EVENT_TX].A_B_CNT = ( serOutBuff.length << 16)
                                      | (                 1 <<  0);
    EDMA0->PARAMSET[EVENT_TX].CCNT         = 0x0001;
    EDMA0->PARAMSET[EVENT_TX].SRC_DST_BIDX = (0x0000 << 16)     // destination B-index
                                            |(0x0001 <<  0);    // source      B-index
    EDMA0->PARAMSET[EVENT_TX].SRC_DST_CIDX = (0x0000 << 16)     // destination C-index
                                            |(0x0000 <<  0);    // source      C-index
    EDMA0->PARAMSET[EVENT_TX].LINK_BCNTRLD = EDMA_PARAM_OPT_LINK_NULL;
    
    EDMA0->IESR = (1 << EVENT_TX);  // EDMA->IPR for EVENT_TX
    EDMA0->EESR = (1 << EVENT_TX);  // Enable Transmit Event
    
    UART->IER = (UART_IER_ETBEI | UART_IER_ERBI);
    UART->FCR = (FIFOEN | DMAMODE1);
    UART->FCR = (FIFOEN | DMAMODE1 | RXFIFTL_4BYTE);
    UART->PWREMU_MGMT |= (UTRST | URRST | FREE);
    

    RX: I think i've undestood your approach of combining EDMA and "manual" UART->RBR handling.

    Can i use the decremented B or C count values from the paramset to calculate the yet recieved number of bytes?

    Configuring the EDMA and UART, i've run into the following issue: despite having a RXFIFTL set to 4 or 8 byte, a EDMA Transferrequest is issued for every single byte. For the testsetup i've just setup a Terminalclient (TeraTerm) and typed in characters. After each character i've updated the watch window which contained the RX-Buffer. See below the code on UART/EDMA:

    // put UART in reset
    UART->PWREMU_MGMT = 0;
    
    // clear Event via: ECR, SECR, EMCR, IECR, EECR and ICR
    ...
    
    // EDMA Paramset RX
    EDMA0->PARAMSET[EVENT_RX].OPT = (EDMA_PARAM_OPT_FWID_8BIT
                                    |EDMA_PARAM_OPT_STATIC_NO
                                    |EDMA_PARAM_OPT_DAM_INCR
                                    |EDMA_PARAM_OPT_SAM_CONST
                                    |EDMA_PARAM_OPT_TCINTEN     // show flag in EDMA->IPR upon completion
                                    |(EVENT_TX << 12));
    EDMA0->PARAMSET[EVENT_RX].SRC     = (uint32)&UART->RBR;
    EDMA0->PARAMSET[EVENT_RX].DST     = (uint32)&serInBuff.buffer[0];
    EDMA0->PARAMSET[EVENT_RX].A_B_CNT = ((MAX_TELEGRAM_LENGTH/4) << 16)   // B-Cnt: Transmit max-size/4 times
                                       |(                     4  <<  0);  // A-Cnt: Transmit 4 Bytes from FiFo
    EDMA0->PARAMSET[EVENT_RX].CCNT         = 0x0001;
    EDMA0->PARAMSET[EVENT_RX].SRC_DST_BIDX = (0x0004 << 16)     // destination B-index
                                            |(0x0000 <<  0);    // source      B-index
    EDMA0->PARAMSET[EVENT_RX].SRC_DST_CIDX = (0x0000 << 16)     // destination C-index
                                            |(0x0000 <<  0);    // source      C-index
    EDMA0->PARAMSET[EVENT_RX].LINK_BCNTRLD = EDMA_PARAM_OPT_LINK_NULL;
    
    UART->IER = (UART_IER_ETBEI | UART_IER_ERBI);
    UART->FCR = (FIFOEN | DMAMODE1);
    UART->FCR = (FIFOEN | DMAMODE1 | RXFIFTL_4BYTE);
    UART->PWREMU_MGMT = (UTRST | URRST | FREE);
    
    EDMA0->IESR = (1 << EVENT_RX);  // EDMA->IPR flag for EVENT_RX
    EDMA0->EESR = (1 << EVENT_RX);  // Enable Recieve Event
    
    UART->LCR &= ~(DLAB);
    UART->IER = (UART_IER_ETBEI | UART_IER_ERBI);
    UART->FCR = (FIFOEN | DMAMODE1);
    UART->FCR = (FIFOEN | DMAMODE1 | RXFIFTL_4BYTE);
    UART->PWREMU_MGMT = (UTRST | URRST | FREE);

  • Do not use CONST mode. Its definition is part of the EDMA3 module's specification, so it is included in most documents that use the EDMA3, but it is not defined for use with any EDMA3 source or destination in the C674x devices. Do not use it. FWID is a don't-care when SAM/DAM=INCR.

    You have the TCC field set to EVENT_TX for both Tx and Rx. This is probably not what you meant to do.

    The Tx and Rx registers are 32-bit registers. The EDMA3 works optimally with 32-bit bus widths, so if you try to sent more than 1 byte with ACNT>1, the EDMA3 will optimize that into 16-bit-wide or 32-bit-wide or multiple 32-bit-wide transfers. For writing multiple bytes to the Tx register, you have to set ACNT=1 and use AB-sync.

    1768011 said:
    Can i use the decremented B or C count values from the paramset to calculate the yet recieved number of bytes?

    Yes, but you will have to be aware that these registers could be in active use when you check them. If the UART is idle when you check these registers, then there will be no problem. But if a new byte comes in between the reading of BCNT and CCNT (or between CCNT and BCNT, depending on how you read them), then your calculations may be incorrect. You may want to temporarily disable the EDMA3 channel by clearing its EER bit, but that might not be sufficient to avoid all race conditions. You may need to study this some and try different scenarios to confirm a solid design.

    On some devices, viewing the Rx buffer may cause a read of the Rx buffer. The C6748 may be new enough that this is not the case, but instead, try typing several characters before reading the Rx buffer location.

    Regards,
    RandyP