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.

TMS570LC4357: TMS570LC4357: SCI DMA RX without IRQ and variable packet lengths

Part Number: TMS570LC4357

Hello,

I am working on TMS570LC4357 eval board and developing the SCI1 RX DMA interface without interrupts for variable byte length packets. I read and am aware of this ticket which mentions the device does not support receiving variable packet lengths: https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/838614/tms570lc4357-how-to-configure-sci-dma-for-receiving-varying-sized-packets

 

However, I tried a simple test with WCP CTCOUNT and I could see that it gets updated once DMA receives some bytes. I was able to use it to track if I received new bytes, however it does not get updated unless a new DMA trigger occurs, so it always holds stale values until arbitrated.

 

I went through this ticket which mentions using PortA/B TR Count as a way to know how many bytes were received: https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1237441/sci-with-dma/4681424?tisearch=e2e-sitesearch&keymatch=%22DMA%22%20AND%20%22length%22%20AND%20%22dynamic%22%20AND%20%22Jagadish%22

 

However, I observed that the Port A/B values always reflect the current transfer the DMA plans to do. In the likely scenario I am using multiple DMA channels with different peripherals, how do I use Port A/B TR count as a way to know how many bytes I have received on a specific SCI RX DMA channel?  I tried using FIFOSTAT registers, but they don't get updated when bytes are received either. 

 

From the other tickets I read on the forum, I am aware that using a protocol is the only way to make this work, however, I would like to know if the FIFO TR or WCP CNT can actually make it work.

 

Regards,

Surabhi

  • Hi Surabhi,

    Apologies for the delayed response!

    We have one internal AI tool which can analyze all the collaterals related to this controller, i got some useful tips from it after feeding this issue.

    Could you please verify whether any one of these solutions is feasible at your end?

    You've correctly identified that the TMS570LC4357's DMA controller doesn't inherently support receiving variable packet lengths without some additional mechanism. Here's my analysis of your observations:

    1. WCP CTCOUNT register: This does update when DMA receives bytes, but as you noted, it only updates when a new DMA trigger occurs, making it unreliable for real-time tracking.

    2. Port A/B TR Count: These values reflect the planned transfer size rather than the actual received bytes count, which isn't helpful for dynamic packet reception.

    3. FIFOSTAT registers: You've found these don't update during DMA reception.

    Possible Solutions

    Since you specifically want to avoid protocol-based approaches, here are some potential workarounds:

    1. Polling-Based Solution with WCP CTCOUNT

    You could implement a polling mechanism that:

    • Configures the DMA for a maximum expected packet size
    • Periodically checks the WCP CTCOUNT to detect when new bytes arrive
    • Uses a timeout mechanism to determine packet completion

    // Configure DMA for maximum expected packet size
    // ...
    
    uint32_t last_count = dmaREG->WCP[channel].CTCOUNT;
    uint32_t timeout_counter = 0;
    const uint32_t TIMEOUT_THRESHOLD = 1000; // Adjust based on baud rate
    
    while(1) {
        uint32_t current_count = dmaREG->WCP[channel].CTCOUNT;
        
        if(current_count != last_count) {
            // New data received
            last_count = current_count;
            timeout_counter = 0;
        } else {
            timeout_counter++;
            if(timeout_counter >= TIMEOUT_THRESHOLD) {
                // No new data for a while, assume packet complete
                uint32_t bytes_received = MAX_PACKET_SIZE - current_count;
                process_packet(bytes_received);
                // Reset DMA for next packet
                break;
            }
        }
        // Small delay
    }

    2. Hybrid Approach with Minimal Interrupts

    While you mentioned avoiding interrupts, a minimal interrupt approach might be more reliable:

    • Configure the SCI RX with a timeout interrupt
    • Use the timeout interrupt to detect end-of-packet conditions
    • Keep DMA running for data transfer

    3. Frame-Based Reception

    If your communication allows it, you could:

    • Use fixed-size frames with header information indicating actual payload size
    • Configure DMA to transfer these fixed-size frames
    • Process only the valid bytes indicated in the header

    Recommendations

    1. Most Reliable: Implement a protocol with packet delimiters or length fields, despite your preference to avoid this.

    2. Next Best: Use the hybrid approach with minimal interrupts, particularly the SCI RX timeout interrupt.

    3. If Constraints Prevent Above: Your approach with WCP CTCOUNT polling could work, but will be less efficient and may miss edge cases.

    The fundamental limitation is that the DMA controller is designed for fixed-length transfers. Any variable-length solution will require some additional mechanism to determine packet boundaries.

    --
    Thanks & regards,
    Jagadish.

  • Thank you for your response.

    I find your solution "Hybrid Approach with Minimal Interrupts" interesting. Could you elaborate a bit on how to implement SCI RX with a timeout interrupt?

    Also it would be good to know: How would we know the number of bytes received (or the number of bytes to copy) if the timeout IRQ was able to detect end of packet condition?

    Thanks,
    Surabhi

  • Hi Surabhi,

    Implementing SCI RX Timeout Interrupt with DMA

    1. Configuring SCI RX Timeout Interrupt

    The timeout interrupt triggers when the RX line goes idle for a specified period. Here's how to implement it:

    Hardware Configuration

    ```c
    // Assuming TI TMS570 or similar MCU with SCI timeout capability

    void SCI_ConfigureRxTimeout(sciBASE_t *sci, uint32_t timeout_bits)
    {
    // Enable timeout functionality
    // Timeout value is typically in bit-times (e.g., 8-16 bit periods)
    sci->SETINTTIMEOUT = timeout_bits; // Example: 10 bit times

    // Enable timeout interrupt
    sci->SETINT = SCI_RX_INT_TIMEOUT;

    // Clear any pending timeout flags
    sci->FLG = SCI_RX_INT_TIMEOUT;
    }

    void SCI_Init_With_Timeout(void)
    {
    // Standard SCI initialization
    sciInit();

    // Configure for 115200 baud, 8N1
    sciSetBaudrate(sciREG1, 115200);

    // Set timeout to ~10 bit times
    // This means if no data arrives for 10 bit periods, timeout triggers
    SCI_ConfigureRxTimeout(sciREG1, 10);

    // Enable SCI and RX
    sciREG1->GCR1 |= 0x80; // Enable SCI
    sciREG1->SETINT |= 0x200; // Enable RX timeout interrupt
    }
    ```

    Timeout Calculation

    ```c
    // For a given baud rate, calculate appropriate timeout
    // Timeout should be > 1 character time but < expected inter-packet gap

    uint32_t Calculate_Timeout_Value(uint32_t baudrate, float char_times)
    {
    // Each character = 10 bits (1 start + 8 data + 1 stop)
    // Timeout in bit-times = char_times * 10

    // Example: 1.5 character times at any baud rate
    return (uint32_t)(char_times * 10.0f);
    }

    // Usage:
    // For detecting end-of-packet after 1.5 character silence:
    uint32_t timeout = Calculate_Timeout_Value(115200, 1.5);
    ```

    2. Determining Number of Bytes Received

    This is the key part! Here are several methods:

    Method A: Using DMA Counter (Most Common)

    ```c
    #define RX_BUFFER_SIZE 256

    uint8_t rx_buffer[RX_BUFFER_SIZE];
    volatile uint32_t packet_size = 0;

    void Setup_DMA_For_RX(void)
    {
    // Configure DMA channel for SCI RX
    dmaChannel_t dma_cfg;

    dma_cfg.SADD = (uint32_t)&sciREG1->RD; // Source: SCI RX register
    dma_cfg.DADD = (uint32_t)rx_buffer; // Dest: our buffer
    dma_cfg.CHCTRL = 0; // Auto-init disabled
    dma_cfg.FRCNT = RX_BUFFER_SIZE; // Frame count (bytes to receive)
    dma_cfg.ELCNT = 1; // Element count
    dma_cfg.ELDOFFSET = 0;
    dma_cfg.FRDOFFSET = 0;
    dma_cfg.PORTASGN = PORTA_READ_PORTB_WRITE;

    dmaSetChEnable(DMA_CH0, DMA_HW);
    dmaSetChannel(DMA_CH0, dma_cfg);
    }

    void SCI_Timeout_ISR(void)
    {
    // Read DMA transfer counter to see how many bytes are LEFT
    uint32_t bytes_remaining = dmaGetTransferCount(DMA_CH0);

    // Calculate bytes actually received
    packet_size = RX_BUFFER_SIZE - bytes_remaining;

    // Clear timeout flag
    sciREG1->FLG = SCI_RX_INT_TIMEOUT;

    // Signal application that packet is ready
    // (set flag, post semaphore, etc.)
    packet_ready_flag = 1;

    // Reset DMA for next packet
    Setup_DMA_For_RX();
    }

    // Application code
    void Process_Packets(void)
    {
    if (packet_ready_flag)
    {
    packet_ready_flag = 0;

    // Now we know exactly how many bytes were received
    Process_Data(rx_buffer, packet_size);
    }
    }
    ```

    Method B: Double Buffer with Pointer Tracking

    ```c
    #define BUFFER_SIZE 256

    uint8_t rx_buffer_A[BUFFER_SIZE];
    uint8_t rx_buffer_B[BUFFER_SIZE];
    uint8_t *active_buffer = rx_buffer_A;
    uint8_t *process_buffer = NULL;

    volatile uint32_t received_count = 0;

    void Setup_DMA_Circular(void)
    {
    // DMA writes to active_buffer
    // On timeout, we swap buffers

    dmaChannel_t dma_cfg;
    dma_cfg.SADD = (uint32_t)&sciREG1->RD;
    dma_cfg.DADD = (uint32_t)active_buffer;
    dma_cfg.FRCNT = BUFFER_SIZE;

    dmaSetChannel(DMA_CH0, dma_cfg);
    }

    void SCI_Timeout_ISR(void)
    {
    uint32_t bytes_remaining = dmaGetTransferCount(DMA_CH0);
    received_count = BUFFER_SIZE - bytes_remaining;

    // Swap buffers
    process_buffer = active_buffer;
    active_buffer = (active_buffer == rx_buffer_A) ? rx_buffer_B : rx_buffer_A;

    // Reconfigure DMA to new buffer
    dmaSetDestAddr(DMA_CH0, (uint32_t)active_buffer);
    dmaSetTransferCount(DMA_CH0, BUFFER_SIZE);

    // Clear flag and signal application
    sciREG1->FLG = SCI_RX_INT_TIMEOUT;
    packet_ready_flag = 1;
    }
    ```

    Method C: Using Hardware Byte Counter (if available)

    ```c
    // Some MCUs have a hardware byte counter register

    void SCI_Timeout_ISR(void)
    {
    // Read hardware byte counter (MCU-specific)
    packet_size = sciREG1->BYTE_COUNT; // Hypothetical register

    // Or use DMA transfer complete count
    packet_size = DMA_CH0->BTCNT; // Bytes transferred count

    // Clear and continue
    sciREG1->FLG = SCI_RX_INT_TIMEOUT;
    packet_ready_flag = 1;
    }
    ```

    Complete Working Example

    Here's a complete implementation:

    ```c
    #define RX_BUFFER_SIZE 512
    #define TIMEOUT_BITS 15 // ~1.5 character times

    uint8_t rx_buffer[RX_BUFFER_SIZE];
    volatile uint32_t packet_length = 0;
    volatile uint8_t packet_ready = 0;

    void Init_SCI_DMA_With_Timeout(void)
    {
    // 1. Initialize SCI
    sciInit();
    sciSetBaudrate(sciREG1, 115200);

    // 2. Configure timeout
    sciREG1->TIMEOUT_CTRL = TIMEOUT_BITS;
    sciREG1->SETINT |= SCI_TIMEOUT_INT;

    // 3. Setup DMA
    dmaChannel_t dma_config = {
    .SADD = (uint32_t)&sciREG1->RD,
    .DADD = (uint32_t)rx_buffer,
    .FRCNT = RX_BUFFER_SIZE,
    .ELCNT = 1,
    .CHCTRL = 0,
    .PORTASGN = PORTA_READ_PORTB_WRITE
    };

    dmaSetChannel(DMA_CH0, dma_config);
    dmaSetChEnable(DMA_CH0, DMA_HW);

    // 4. Enable interrupts
    vimEnableInterrupt(SCI_TIMEOUT_INT_CHANNEL, SYS_IRQ);

    // 5. Start SCI
    sciREG1->GCR1 |= 0x80;
    }

    #pragma INTERRUPT(SCI_Timeout_Handler, IRQ)
    void SCI_Timeout_Handler(void)
    {
    // Get remaining count from DMA
    uint32_t remaining = dmaREG->BTCR[DMA_CH0];

    // Calculate received bytes
    packet_length = RX_BUFFER_SIZE - remaining;

    // Only process if we received data
    if (packet_length > 0)
    {
    packet_ready = 1;

    // Stop DMA temporarily
    dmaSetChEnable(DMA_CH0, DMA_SW);

    // Will be restarted after processing
    }

    // Clear timeout flag
    sciREG1->FLG = SCI_TIMEOUT_FLAG;
    }

    void Main_Task(void)
    {
    Init_SCI_DMA_With_Timeout();

    while(1)
    {
    if (packet_ready)
    {
    // Process the received packet
    Process_Packet(rx_buffer, packet_length);

    // Reset for next packet
    packet_ready = 0;
    packet_length = 0;

    // Restart DMA
    dmaSetTransferCount(DMA_CH0, RX_BUFFER_SIZE);
    dmaSetChEnable(DMA_CH0, DMA_HW);
    }

    // Other tasks...
    }
    }
    ```

    Key Points

    1. **Timeout Detection**: The hardware automatically detects when RX line is idle for the specified bit-times
    2. **Byte Count**: Read the DMA counter register to determine `bytes_received = buffer_size - bytes_remaining`
    3. **Minimal Overhead**: Only one interrupt per packet, not per byte
    4. **Reliable**: Hardware timeout is more accurate than software polling

    This approach gives you the efficiency of DMA with reliable packet boundary detection!

    --
    Thanks & regards,
    Jagadish.

  • Hi Jagadish,

    Thank you for the detailed response and for taking the time to analyze my observations — I appreciate the effort.

    I wanted to clarify one specific point and align it with what I am seeing in both the TRM and on hardware.

    SCI timeout / idle interrupt support

    After re-checking the TMS570LC43x Technical Reference Manual and the SCI register map, I do not see support for a programmable SCI RX timeout interrupt on this device.

    Specifically:

    • There is no register in the SCI module to configure an RX inactivity timeout (for example, timeout after N bit-times of silence).
    • The SCI interrupt sources are limited to RX ready, TX ready, error conditions, wake-up/idle detect, and break detect.
    • The wake-up / idle detection mechanism appears to be status-based and intended for address-mark or low-power wake use, not for programmable end-of-packet timeout detection, and it is not associated with any configurable counter.

    I could not find any timeout-related bit or counter in SCIGCRx, SCISETINT, SCIFLR, or related SCI registers that would behave like a true RX timeout interrupt.

    DMA + WCP / transfer count observation

    As discussed earlier, my testing confirms that:

    • WCP.CTCOUNT only updates when the DMA channel is arbitrated.
    • With multiple active DMA channels, the value can remain stale even while SCI RX data continues to arrive.
    • Port A/B transfer count registers reflect the configured transfer size, not the number of bytes already received.

    Because of this, I still do not have a reliable way to determine how many bytes have actually been transferred by the SCI RX DMA channel at any given time, nor does the SCI hardware itself appear to provide a mechanism to signal end-of-packet or inactivity without interrupts or protocol framing.

    My question is therefore:
    Is there any hardware-supported method on the TMS570LC4357 to determine SCI RX DMA progress or packet completion—beyond fixed-length DMA transfers, protocol-level delimiters, or interrupts?
    So far, I have not found one in the TRM or through experimentation.

    Conclusion

    Based on the above, my current understanding is:

    • The TMS570LC4357 does not support an SCI RX timeout interrupt in hardware.
    • Variable-length SCI RX using DMA appears to require either:
      • a protocol-level delimiter or length field, or
      • the use of SCI or DMA interrupts to detect packet boundaries.

    If I have missed a specific register, errata note, or documented mechanism that enables this use case, I would really appreciate a pointer to the exact TRM section or register description so I can review it.

    Thanks again for your time and guidance.

    Regards,
    Surabhi

  • Hi Surabhi,

    Apologies for the delayed response!

    • The TMS570LC4357 does not support an SCI RX timeout interrupt in hardware.
    • Variable-length SCI RX using DMA appears to require either:
      • a protocol-level delimiter or length field, or
      • the use of SCI or DMA interrupts to detect packet boundaries.

    Your understanding is totally correct.

    You can refer below thread as well for same confirmation:

    (+) TMS570LC4357-EP: TMS570LC4357 SCI — is there an idle-line RX interrupt? - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums

    --
    Thanks & regards,
    Jagadish.