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:can_sendMessage () 和 can_clearInterruptStatus () 之间可能存在 IF1 寄存器竞争?

Guru**** 2953920 points

Other Parts Discussed in Thread: C2000WARE

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1650597/tms320f28388d-tms320f2838x-possible-if1-register-race-between-can_sendmessage-and-can_clearinterruptstatus

器件型号: TMS320F28388D
主题: C2000WARE 中讨论的其他器件

工具/软件:C2000Ware 26.00.00.00 driverlib  

摘要

我们在内部观察到一种罕见的断言失败 CAN_sendMessage() 、并试图了解根本原因。 我们注意到 CAN_sendMessage() 并 CAN_clearInterruptStatus() 使用 IF1 接口寄存器、我们想知道当从不同的优先级上下文调用这两者时、这是否会导致竞态条件。

我们的设置

  • 使用 CAN_readMessage() (IF2) 和 CAN_clearInterruptStatus() (IF1) 在 CAN ISR 中读取 CAN RX 消息
  • CAN TX 消息使用 CAN_sendMessage() (IF1) 从周期性后台任务发送

我们理解一般建议是将 IF1 用于 TX、将 IF2 用于 RX 以避免冲突(请参阅有关 IF1/IF2 使用的 e2e 主题)。 我们注意到 CAN_clearInterruptStatus() 使用了 IF1、即使在本例中它是从 RX ISR 上下文调用的。

观察到的行为

  • CAN_sendMessage()  __error__() 断言检查之一进行命中
  •  __error__() 回调报告 can.c 中的第 450 行、该行为空行。 调用堆栈指向第 455 行ASSERT((objID <= 32U) && (objID > 0U))()。 我们怀疑调试信息可能不准确、实际失败的断言可能是中的三个中的任何一个 CAN_sendMessage()、包括第 482 行的 DLC 检查。
  • 我们在 base objID msgLen 调试器中验证了、和—所有值在出现故障时都是正确的。
  • 消息对象配置正确(在调试器中验证了参数)
  • 此故障非常罕见且是间歇性的、与时序相关的问题相一致

潜在比赛情景

CAN_sendMessage() 对 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);

我们的担心是:如果 CAN RX ISR 在步骤 2 和步骤 3 之间触发、 CAN_clearInterruptStatus() 会为不同的消息对象向 IF1CMD 写入新命令:

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

我们认为 CAN_sendMessage() 当 IF1MCTL 恢复时、可能会使用错误消息对象中的数据读回 IF1MCTL、从而导致步骤 4 中的 DLC 不匹配。 BUSY 位检查在这里不起作用、因为它仅保护 IF1 到 RAM 的传输、而不保护多步序列。

问题

  1.  CAN_sendMessage() 和之间的这种 IF1 共享是否 CAN_clearInterruptStatus() 确实会导致我们看到的问题?
  2. 是否有 CAN_clearInterruptStatus() 使用 IF1 而不是 IF2 的具体原因、或者这是我们可能误解的事情?
  3. 作为一种权 CAN_sendMessage() 变措施、我们正在考虑在调用周围短暂禁用 CAN PIE 中断。 您会推荐这种方法、还是有更好的做法?
Interrupt_disable(CAN_CMD_INT0);
CAN_sendMessage(CAN_INTERFACE_BASE, msg_obj_id, msg_len, raw_data);
Interrupt_enable(CAN_CMD_INT0);

感谢您的分享。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Steffen:

    您的分析是正确的 — 这可能来自您描述的比赛条件。

    Q1:是的,这是根本原因。 can_sendMessage() 中的 busy-bit 检查仅在步骤 2 中保护 IF1->RAM 传输。 一旦 BUSY 清除、IF1CMD 就会空闲—如果 RX ISR 在那一点触发、can_clearInterruptStatus () 会立即传递其自己的预写 Busy-wait(BUSY 已清除)、用不同的对象编号覆盖 IF1CMD、并触发新的传输。 当 can_sendMessage() 在步骤 3 中恢复时、它读取 IF1MCTL 的错误对象、并且 DLC 声明失败。 间歇性特性与这种窄的抢占窗口一致。

    问题 2:使用 IF1 的 can_clearInterruptStatus () 与 IF1=TX / IF2=RX 约定不一致、这是一个已知的 driverlib 问题。 由于该函数仅写入无回读的单个命令字、因此它可以安全地改用 IF2。

    问题 3:您建议的解决方法有效。 更简洁的修复方法是在本地修改 can_clearInterruptStatus() 以使用 IF2、从而完全消除争用:

    //在 can_clearInterruptStatus () 中将 IF1 替换为 IF2 (~can.c 行 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){}

    由于 can_readMessage () 和 can_clearInterruptStatus () 是在同一 ISR 中按顺序调用的,因此使用 IF2 是安全的 — 在单个 ISR 上下文中不存在抢占问题。

    如果无法修改 driverlib、则 另一种选择是按您建议的方式禁用 can_sendMessage () 周围的 CAN PIE 中断。

    此致、

    Joseph

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Joseph:

    感谢您的快速而彻底的回应—这证实了我们的怀疑。

    我们在 HAL 层中添加了一个本地包装器,它镜像 can_clearInterruptStatus (),但使用 IF2 而不是 IF1。 这样我们就可以兼容未来的 C2000Ware 更新、而无需修补 driverlib。

    作为一个建议:在未来的 C2000Ware 版本中,可能值得单独提供 can_clearInterruptStatus () 的 IF1 和 IF2 变体,并将当前函数映射到 IF1 以实现向后兼容性。 这样、用户就能以干净的方式在不使用自定义包装的情况下分离使用 TX 和 RX 接口寄存器。

    再次感谢您的支持。

    此致、
    Steffen