LAUNCHXL-F280039C: BOOTLOADER for 2 MCU via PC

Part Number: LAUNCHXL-F280039C
Other Parts Discussed in Thread: TMS320F280039C

Hello Experts,

Currently, my circuit have 2 MCU TMS320F280039C and I am implementing the bootloader for these MCUs with structure PC --> (SCIA)MCU1(SCIB) --> (SCIA)MCU2. I use serial flash programmer from TI to do 2 steps as below:

Step1: PC will boot MCU1 via SCIA. I follow as TI guiline "Serial Flash Programming of C2000™ Microcontrollers" and I can implement bootloader for MCU1 OK

Step2: PC will boot MCU2 via MCU1. In this case, MCU1 will be a UART brigde, just get signal from PC via SCIA and SCIB will get signal from SCIA after that transfer to MCU2 as below:

        rxStatus1 = SCI_getRxStatus(SCIA_BASE);
        rxStatus2 = SCI_getRxStatus(SCIB_BASE);
        if(rxStatus1 || rxStatus2)
        {
            while(!SCI_isDataAvailableNonFIFO(SCIA_BASE) && !SCI_isDataAvailableNonFIFO(SCIB_BASE))
            {
            }

            if(SCI_isDataAvailableNonFIFO(SCIA_BASE) == 1)
            {
                receivedChar1 = ((uint16_t)(HWREGH(SCIA_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M));
                SCI_writeCharNonBlocking(SCIB_BASE, receivedChar1);
            }
            if(SCI_isDataAvailableNonFIFO(SCIB_BASE) == 1)
            {
                receivedChar2 = ((uint16_t)(HWREGH(SCIB_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M));
                SCI_writeCharNonBlocking(SCIA_BASE, receivedChar2);
            }
        }

I also use  TI guiline "Serial Flash Programming of C2000™ Microcontrollers" to do bootloader for MCU2. However,  sometimes the command prompt indicate 0xAA = 0xAA and PC cannot send command to MCU2 in the next command as below picture. Please help me sharing your suggestions.

Thank you 33a19fab-b9d1-4976-bb50-f99ca26e8099.jpg

  • Hello,

    Apologies for the delay, please expect a reply on Monday.

    Best,
    Matt

  • Hello,

    This seems to be a timing and latency issue in MCU1's SCI bridge code. The SCI bootloader protocol requires that after each data transfer, the bootloader echoes back every 8-bit character received to the host for verification. Your polling-based bridge introduces delays that cause the PC-side Serial Flash Programmer to timeout waiting for MCU2's echo response, which is why it intermittently stalls after the autobaud handshake (0xAA = 0xAA) succeeds, but subsequent commands fail.

    Your intermittent success occurs when the polling loop happens to be at the right point in its cycle when data arrives — the timing aligns by chance. At other times, the loop is blocked in the `while()` or processing the wrong direction, causing the echo to arrive late and the Serial Flash Programmer to timeout.

    1. The while() blocking loop creates a race condition:

    while(!SCI_isDataAvailableNonFIFO(SCIA_BASE) && !SCI_isDataAvailableNonFIFO(SCIB_BASE))

    This blocks until data arrives on either port, but if data arrives on both ports nearly simultaneously (which happens during echo-back), you may process only one direction per loop iteration, causing the other direction's response to be delayed or missed.

    2. Non-blocking write without TX-ready check:

    SCI_writeCharNonBlocking(SCIB_BASE, receivedChar1);

    If the TX buffer is already full, this call will silently drop the character. You're not verifying that the transmitter is ready before writing.

    3. The bootloader protocol is bidirectional and time-sensitive:
    After autobaud lock, the device expects a KeyValue (0x08AA) immediately following the autobaud echo. The Serial Flash Programmer has tight timeout windows—your bridge must relay bytes in both directions with minimal latency, or the host interprets a missing/delayed echo as a failure.

    Recommended Fixes:

    Option A: Interrupt-Driven Bridge (Recommended)

    Replace your polling loop with interrupt-driven FIFO transfers:

    // SCIA RX Interrupt - forward to SCIB
    __interrupt void SCIA_RX_ISR(void)
    {
    uint16_t data = SCI_readCharNonBlocking(SCIA_BASE);
    while(!SCI_isSpaceAvailableNonFIFO(SCIB_BASE)); // Wait for TX ready
    SCI_writeCharBlockingNonFIFO(SCIB_BASE, data);
    // Clear interrupt
    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXRDY_BRKDT);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
    }
    
    // SCIB RX Interrupt - forward to SCIA
    __interrupt void SCIB_RX_ISR(void)
    {
    uint16_t data = SCI_readCharNonBlocking(SCIB_BASE);
    while(!SCI_isSpaceAvailableNonFIFO(SCIA_BASE)); // Wait for TX ready
    SCI_writeCharBlockingNonFIFO(SCIA_BASE, data);
    // Clear interrupt
    SCI_clearInterruptStatus(SCIB_BASE, SCI_INT_RXRDY_BRKDT);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
    }

    Option B: Fix the Polling Loop (Minimum Fix)

    If you must use polling, fix these critical issues:
    for(;;)
    {
    // Check SCIA RX -> forward to SCIB
    if(SCI_isDataAvailableNonFIFO(SCIA_BASE))
    {
    receivedChar1 = (uint16_t)(HWREGH(SCIA_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M);
    while(!SCI_isSpaceAvailableNonFIFO(SCIB_BASE)); // CRITICAL: wait for TX ready
    SCI_writeCharNonBlocking(SCIB_BASE, receivedChar1);
    }
    
    // Check SCIB RX -> forward to SCIA
    if(SCI_isDataAvailableNonFIFO(SCIB_BASE))
    {
    receivedChar2 = (uint16_t)(HWREGH(SCIB_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M);
    while(!SCI_isSpaceAvailableNonFIFO(SCIA_BASE)); // CRITICAL: wait for TX ready
    SCI_writeCharNonBlocking(SCIA_BASE, receivedChar2);
    }
    }

    Key changes:
    - Remove the outer `if(rxStatus1 || rxStatus2)` check and the blocking `while()` — these add unnecessary latency
    - Add TX-ready checks before writing to prevent dropped characters
    - Tight continuous loop — don't gate entry on RX status flags

    Additional Recommendations: run bridge code from RAM, not flash, to minimize execution latency

    Best,

    Matt

  • Hello Matt,

    Thank you for your feedback. I followed your suggestions. However, options A the cmd is stopped at 41==0 as below

    and in option B, the signal cannot send to cmd as below

    Do you have any other suggestions or other methodS to implement bootloader for MCU2 via MCU1?

  • Hello,

    Please allow me a few days to discuss this with my colleague.

    Best,
    Matt

  • Hello,

    A few clarification questions:

    A failure (41==0) — device type used:
    In your Option A screenshot, the command uses -d f28002x from the f280025 directory. Can you clarify that the MCUs are TMS320F280039C, which is an F28003x device, not F28002x? The F28002x flash kernel is incompatible. Did you re-run Option A using -d f28003x with the f280039C kernel?

    B failure — SCIB initialization:
    When Option B fails entirely (no aa==aa output), the most common cause is that while(!SCI_isSpaceAvailableNonFIFO(SCIB_BASE)) is spinning forever because SCIB TX is never ready. Please verify:

    1. SCIB baud rate on MCU1 is configured to 9600 baud (matching the -b 9600 argument)
    2. GPIO mux for SCIB TX/RX pins is set to SCI function (not GPIO mode)
    3. SCIB mode is non-FIFO (consistent with SCI_isDataAvailableNonFIFO / SCI_isSpaceAvailableNonFIFO API calls)
    4. MCU2 BOOT pins are configured for SCIA boot mode per the F28003x TRM boot mode table
    5. SCI TX/RX physical connections

    To isolate the issue, add a simple GPIO toggle in MCU1's bridge loop so you can confirm with an oscilloscope or LED whether the code is reaching the bridge at all, or whether it's stuck inside the while() wait.

    Best Regards,

    Allison