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 byCAN_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 inCAN_sendMessage(), including the DLC check at line 482. - We verified
base,objID, andmsgLenin 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
- Could this IF1 sharing between
CAN_sendMessage()andCAN_clearInterruptStatus()indeed cause the issue we are seeing? - Is there a specific reason
CAN_clearInterruptStatus()uses IF1 rather than IF2, or is this something we may be misunderstanding? - 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.