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.

TMS320F28388D: TMS320F2838x: Possible IF1 register race between CAN_sendMessage() and CAN_clearInterruptStatus()?

Part Number: TMS320F28388D
Other Parts Discussed in Thread: C2000WARE

Tool/software: C2000Ware 26.00.00.00 driverlib 

Summary

We are observing a rare assertion failure inside CAN_sendMessage() and are trying to understand the root cause. We noticed that both CAN_sendMessage() and CAN_clearInterruptStatus() use the IF1 interface registers, and we are wondering whether this could lead to a race condition when the two are called from different priority contexts.

Our setup

  • CAN RX messages are read in the CAN ISR using CAN_readMessage() (IF2), followed by CAN_clearInterruptStatus() (IF1)
  • CAN TX messages are sent from a periodic background task using CAN_sendMessage() (IF1)

We understand the general recommendation is to use IF1 for TX and IF2 for RX to avoid conflicts (see e2e thread on IF1/IF2 usage). We noticed that CAN_clearInterruptStatus() uses IF1 even though in our case it is called from the RX ISR context.

Observed behavior

  • CAN_sendMessage() hits __error__() via one of its ASSERT checks
  • The __error__() callback reports line 450 in can.c, which is an empty line. The call stack points to line 455 (ASSERT((objID <= 32U) && (objID > 0U))). We suspect the debug info may be imprecise and the actual failing assertion could be any of the three in CAN_sendMessage(), including the DLC check at line 482.
  • We verified baseobjID, and msgLen in the debugger — all values are correct at the time of the fault.
  • The message object is correctly configured (parameters verified in debugger)
  • The failure is rare and intermittent, consistent with a timing-dependent issue

Potential race scenario

CAN_sendMessage() performs a multi-step sequence on IF1:

// Step 1: Request readback of message object control (can.c:464)
HWREG_BP(base + CAN_O_IF1CMD) = ((uint32_t)CAN_IF1CMD_CONTROL |
                                  (objID & CAN_IF1CMD_MSG_NUM_M));

// Step 2: Wait for transfer (can.c:470)
while((HWREGH(base + CAN_O_IF1CMD) & CAN_IF1CMD_BUSY) == CAN_IF1CMD_BUSY)
{
}

// Step 3: Read back DLC (can.c:477)
msgCtrl = HWREGH(base + CAN_O_IF1MCTL);

// Step 4: Assert DLC matches (can.c:482)
ASSERT((msgCtrl & CAN_IF1MCTL_DLC_M) == msgLen);

Our concern is: if the CAN RX ISR fires between step 2 and step 3, CAN_clearInterruptStatus() would write a new command to IF1CMD for a different message object:

// CAN_clearInterruptStatus (can.c:238)
HWREG_BP(base + CAN_O_IF1CMD) = ((uint32_t)CAN_IF1CMD_CLRINTPND |
                                 (intClr & CAN_IF1CMD_MSG_NUM_M));

We believe this could cause CAN_sendMessage() to read back IF1MCTL with data from the wrong message object when it resumes, leading to the DLC mismatch at step 4. The busy-bit check would not help here since it only guards the IF1-to-RAM transfer, not the multi-step sequence.

Questions

  1. Could this IF1 sharing between CAN_sendMessage() and CAN_clearInterruptStatus() indeed cause the issue we are seeing?
  2. Is there a specific reason CAN_clearInterruptStatus() uses IF1 rather than IF2, or is this something we may be misunderstanding?
  3. As a workaround, we are considering disabling the CAN PIE interrupt briefly around CAN_sendMessage() calls. Would you recommend this approach, or is there a better practice?
Interrupt_disable(CAN_CMD_INT0);
CAN_sendMessage(CAN_INTERFACE_BASE, msg_obj_id, msg_len, raw_data);
Interrupt_enable(CAN_CMD_INT0);

Thank you for any insight.

  • Hi Steffen,

    Your analysis is correct - this could be from a race condition that you've described.

    Q1: Yes, this is the root cause. The busy-bit check in CAN_sendMessage() only guards the IF1->RAM transfer at step 2. Once BUSY clears, IF1CMD is free - if the RX ISR fires at that point, CAN_clearInterruptStatus() passes its own pre-write busy-wait immediately (BUSY is already clear), overwrites IF1CMD with a different object number, and triggers a new transfer. When CAN_sendMessage() resumes at step 3, it reads IF1MCTL for the wrong object and the DLC assertion fails. The intermittent nature is consistent with this narrow preemption window.

    Q2: CAN_clearInterruptStatus() using IF1 is inconsistent with the IF1=TX / IF2=RX convention and is a known driverlib issue. Since the function only writes a single command word with no readback, it can safely use IF2 instead.

    Q3: Your proposed workaround is valid. A cleaner fix is to modify CAN_clearInterruptStatus() locally to use IF2, which eliminates the contention entirely:

    // Replace IF1 with IF2 throughout CAN_clearInterruptStatus() (~can.c line 238)
    while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) == CAN_IF2CMD_BUSY) {}
    HWREG_BP(base + CAN_O_IF2CMD) = ((uint32_t)CAN_IF2CMD_CLRINTPND |
    (intClr & CAN_IF2CMD_MSG_NUM_M));
    while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) == CAN_IF2CMD_BUSY) {}

    Since CAN_readMessage() and CAN_clearInterruptStatus() are called sequentially within the same ISR, using IF2 for both is safe - there's no preemption concern within a single ISR context.

    If modifying driverlib is not an option, disabling the CAN PIE interrupt around CAN_sendMessage() as you proposed is a another alternative.

    Regards,

    Joseph

  • Hi Joseph,

    Thank you for the quick and thorough response — this confirms what we suspected.

    We added a local wrapper in our HAL layer that mirrors CAN_clearInterruptStatus() but uses IF2 instead of IF1. This keeps us compatible with future C2000Ware updates without patching driverlib.

    As a suggestion: it might be worth providing separate IF1 and IF2 variants of CAN_clearInterruptStatus() in future C2000Ware versions, with the current function mapping to IF1 for backwards compatibility. This would give users a clean way to separate TX and RX interface register usage without custom wrappers.

    Thanks again for the support.

    Best regards,
    Steffen