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.

[参考译文] IPC-AM243X:MCU-PLUS-SDK 在另一端尚未完成读取 Vring 时发送数据包

Guru**** 2424930 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1350501/mcu-plus-sdk-am243x-ipc-does-send-packages-while-the-other-side-has-not-finished-reading-the-vring

器件型号:MCU-PLUS-SDK

您好!

我们使用的是 MCU 加上使用 LTS 3.2.0编译的 SDK 09.00。

我们的 IPC 有一个问题、我们花了几周时间才得以解决、这个问题真的很难重现、更不用说创建极少的示例了、因此我们需要一些帮助、以便找到一个良好的实践来继续调试这个问题。

因此、经过大量调试后、我们确定了要在 SDK 的 RPMessage/IpcNotife-drivers 下解决的此问题。 我们注意到一个内核尝试通过 RPMessage 将一个软件包写入到 vring 中、而另一个内核没有读取该数据包。

我们将 RPMessage 用于内核到内核的通信。

如果一个内核发送数据包、则其他内核将获得中断并调用回调。 在该仍处于中断上下文的回调中、我们将一个值写回振铃缓冲区到数据包的用户空间中、因此我们知道该数据包已经被读取。 在每一次发送的另一侧、我们会检查该值是否存在。 如果该值不存在、则表示未读取数据包。

碰巧值没有写入、这意味着数据包没有被读取、但其他内核会尝试在该位置进行迭代、我们无法确定发生这种情况的原因。 这只会不时发生。 有时很早、有时有点晚。

我还尝试通过 IPC 使用重负载通信的另一个项目来验证这一点、但遗憾的是、我无法重现此问题、因此我们甚至无法提供极少的示例。

您对我们如何从这里着手有任何想法吗? 我们还可以进入调试会话。

此致

费利克斯

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

    尊敬的  Felix:

    我正在查看您的问题,您可能希望在一两天内得到回复。

    此致、

    S.Anil.

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

    尊敬的  Felix:

    很抱歉我正忙于其它升级,因此延迟回复。

    如果一个内核发送数据包,则另一个内核将获得中断,并调用回调。 在该仍处于中断上下文的回调中、我们将一个值写回振铃缓冲区到数据包的用户空间中、因此我们知道该数据包已经被读取。 在每一次发送的另一侧、我们会检查该值是否存在。 如果该值不存在,则表示未读取数据包。

    您的理解是正确的。 这就是 IPC 的工作原理。  

    Unknown 说:
    如果没有写入值,则表明没有读取数据包,但其他内核尝试在该位置吸住,我们无法确定为什么会发生这种情况。 这只会不时发生。 有时很早,有时有点晚。[/报价]

    实际上、每个内核都有用于 TX 和 RX 的唯一缓冲器。 因此、不可能一个内核 在同一分配的缓冲区中写入和读取。

    我需要一些更多详细信息来调试该问题。

    1.您是否修改了 IPC 驱动程序? 如果您修改了驱动程序、请分享您的代码。

    2. IPC 用于您的应用程序中的哪些内核之间?

    3.如果远程内核接收到数据,请检查缓冲区中的 IPC 共享地址值,看看共享数据是否可用?

    此致、

     Anil.

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

    Anil、您好!

    感谢您的答复。 我们修改了 IPC 驱动程序。

    1.

    int32_t RPMessage_send( void*    data,
                            uint16_t dataLen,
                            uint16_t remoteCoreId,
                            uint16_t remoteEndPt,
                            uint16_t localEndPt,
                            uint32_t timeout
                          )
    {
        int32_t status = SystemP_FAILURE;
    
        if(remoteCoreId < CSL_CORE_ID_MAX && gIpcRpmsgCtrl.isCoreEnable[remoteCoreId]
            && data != NULL && dataLen != 0
            )
        {
            uint16_t vringBufId;
    
            status = RPMessage_vringGetEmptyTxBuf(remoteCoreId, &vringBufId, timeout);
            if(status == SystemP_SUCCESS)
            {
                uint8_t *vringBufAddr = RPMessage_vringGetTxBufAddr(remoteCoreId, vringBufId);
                uint16_t vringBufLen = RPMessage_vringGetTxBufLen(remoteCoreId, vringBufId);
                RPMessage_Header *header = (RPMessage_Header *)vringBufAddr;
    
                if(dataLen > (vringBufLen - sizeof(RPMessage_Header)) )
                {
                    dataLen = vringBufLen - sizeof(RPMessage_Header);
    
                    DebugP_logWarn("[IPC RPMSG] Message send to remote core %d @ %d end point truncated due to lack of space in vring buffer !!!\r\n",
                        remoteCoreId, remoteEndPt);
                }
    
                header->srcEndPt = localEndPt;
                header->dstEndPt = remoteEndPt;
                header->srcCoreId = gIpcRpmsgCtrl.selfCoreId;
                header->flags = 0;
                header->dataLen = dataLen;
    
                uint32_t dummy = 0;
                memcpy(&dummy, &vringBufAddr[sizeof(RPMessage_Header)+4], sizeof(uint32_t));
    
                DebugP_assert((dummy == 0) || (dummy == 0xABCDEF12));
    
    
                memcpy( &vringBufAddr[sizeof(RPMessage_Header)], data, dataLen);
    
                status = RPMessage_vringPutFullTxBuf(remoteCoreId, vringBufId, dataLen + sizeof(RPMessage_Header), timeout);
    
                if(status != SystemP_SUCCESS)
                {
                    DebugP_logWarn("[IPC RPMSG] Message send to remote core %d @ %d end point failed due to lack of space in Notify Queue !!!\r\n",
                    remoteCoreId, remoteEndPt);
                }
            }
            else
            {
                DebugP_logWarn("[IPC RPMSG] Message send to remote core %d @ %d end point failed due to lack of space in vring !!!\r\n",
                    remoteCoreId, remoteEndPt);
            }
        }
        else
        {
            DebugP_logError("[IPC RPMSG] Message send to remote core %d @ %d end point failed due to invalid parameters !!!\r\n",
                remoteCoreId, remoteEndPt
                );
        }
        return status;
    }

    每当我们收到一个数据包时、我们都会进行处理、处理该数据包后、我们会在 IPC 的回调中写回一个模式(0xABCDEF12)、因此仍然在 IPC 中断的中断上下文中。 下次我们发送时、恰好在 IPC_rpmsg 中发送该模式之前、我们检查该模式是否存在。 这由两个内核以相同的方式完成。

    2.它介于 mcu0_0和 mcu1_0之间(也就是说、我们在应用启动后立即将另一个固件加载到第二个集群中、这是必需的、因为在集群启动后、我们无法将其加载到 mcu0_1中。)

    3.如果您答对了,我们会进行检查,并且每个内核上的每个 coreObj 的共享地址都匹配。 因此、一个内核 Rx 是另一个内核 TX、反之亦然。

    我们目前的情况是:


    因此、对此进行了一些解释:

    1.我们启用了全局断点,我在抛出 assert 后当时设置了 HW 断点(因此在 DebugP_assert ()内),因此另一个内核也会停止。

    2.配置为:12个缓冲区,每个128字节,端点号为2。 我们在 RPMessage 标头之后有自己的32位大标头。

    3.在右边,你可以看到 vring 的记忆。 缓冲器的起始地址为0x701D8140

    4.我们所做的检查是、我们是否已将缓冲区的数据复制到我们的虚拟变量、即12或0x0C。 由于这不是模式,因此抛出 assert。

    5.正如您在记忆视图中看到的:模式是存在的! 它位于地址为0x701D3C0的数据包中。

    在我的结论中、这似乎是运行时问题、因为在从缓冲区复制值和抛出断言之间、另一个内核找到了处理此数据包的时间并同时写回模式。 全局断点确实起作用、但似乎"太晚了"。

    这也适用于我们一开始遇到的问题、因为当我们收到一些值时、有些值是错误的、可能是因为一个内核写入缓冲区、而另一个内核当前正在读取该值、所以会发生这种情况。

    在我理解的处理-读取 vring、把它拿到队列包等-是安全的,因为索引处理,但不知何故,我们在这里遇到了一个问题。 是否有可能与长时间锁定自另一个实体的中断有关?

    此致、

    费利克斯

    编辑:

    我们现在有一个想法,可能会发生什么错误:我们注意到 idx 值永远不会减少或检查或任何事情。 它们只是递增、然后执行取模。 这意味着12个缓冲区的值是不可能的、因为这样就会出现一个非常奇数的值、即在到达最大 uint16_t 值(65536 / 65535)的末尾时、在溢出回0之前、必须以安全方式返回。 我们认为这在某种程度上扰乱了 idx 处理。 我们现在正在研究8个和16个缓冲器

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

    嗨、Felix:

    感谢您分享以上详细信息。

    1.我们启用了全局断点,我在抛出 assert 后当时设置了 HW 断点(因此在 DebugP_assert ()内),因此另一个内核也会停止。

    2.配置为:12个缓冲区,每个128字节,端点号为2。 我们在 RPMessage 标头之后有自己的32位大标头。

    3.在右边,你可以看到 vring 的记忆。 缓冲器的起始地址为0x701D8140

    4.我们所做的检查是、我们是否已将缓冲区的数据复制到我们的虚拟变量、即12或0x0C。 由于这不是模式,因此抛出 assert。

    5.正如您在记忆视图中看到的:模式是存在的! 它位于地址为0x701D3C0的数据包中。

    [/报价]

    我理解以上所有要点。 您正在将数据包从主内核(R5F0_0)发送到远程内核(R5F1_0)、并且远程内核读取数据、 同样,它会将魔术标志(0xABCDEF12)数据发送到主内核。在主内核端,每次都要基于这个标志发送数据。 如果此标志没有魔术值(0xABCDEF12)、那么您将插入调试生效、否则您将再次开始新的传输。

    所以,我的问题是,你发送的频率有多高?

    我假设您首先调用 RPMessage_send、然后调用 RPMessage_recv API。

    如下所示。

    我们现在有一个想法,什么可能会出错:我们注意到 idx-value 永远不会减少或检查或任何东西。 它们只是递增、然后执行取模。 这意味着12个缓冲区的值是不可能的、因为这样就会出现一个非常奇数的值、即在到达最大 uint16_t 值(65536 / 65535)的末尾时、在溢出回0之前、必须以安全方式返回。 我们认为这在某种程度上扰乱了 idx 处理。 我们现在正在调查8个和16个缓冲区

    您能详细说明一下吗? 我真的不明白这一点。

    如果您删除应用程序代码并保留唯一的 IPC 代码、我也没问题。

    如果您在公共论坛共享您的代码有任何问题,请尝试与私人消息共享代码,或联系您的 FAE 与我共享您的代码外您的应用程序.

    此致、

    S.Anil.

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

    Anil、您好!

    我们每1ms 发送一次、一些呼叫以太网发送、所以可能每个500 µs 都发送了一个数据包。

    RPMessage_send 在 FreeRTOS 任务的任务上下文中完成、而接收在 RPMessage 的回调中完成、该回调可以在中断上下文中进行注册。

    最后一点、我认为这正是我们的问题。 我们现在使用的缓冲器数量是8、16和32、我们不再看到这些问题。 只有数量的缓冲区可以被用于一个版本65536 /数量的缓冲区没有(!) 使用余数是合适的。

    我的意思是、RPMessage 在内部使用了一个 VRING 的索引。 此索引用于计算要使用模数处理的当前数据包。 例如、对于此时的 lastAvailIdx:

    可以在 RPMessage_vringGetEmptyTxBuf 中。 使用 vringObj 的其他索引也是如此。 只有增量、从不是递减。 因此、根据设计、该值会溢出并再次从0开始。 如果您现在有一个12表示 vringNumBuf、则存在边沿情况、其中模数不会在正常情况下得到0、例如就在溢出之前、因此可能会丢失一个数据包。 2、4、8、16和32等的情况并非如此(因此全部为2^n 个案例)。 不知何故、这似乎是它在处理数据包时混合了 smth 的原因。

    因此、我们当前的案例运行了4个小时、而之前的问题发生在80年代之后、甚至更早、并且始终可以重现。

    我认为对于使用 SYSCFG 工具的人来说、这绝不是问题、因为据我所知、这不提供除8、16等选项之外的任何选项。 这对我们来说只是个问题、因为在有关 IPC 的一个主题中、一位同事表示 IPC 不要求是2^n: https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1319529/mcu-plus-sdk-am243x-possible-packet-loss-in-ipc/5037866?tisearch=e2e-sitesearch&keymatch=%25252525252525252520user%2525252525252525253A453845#5037866

    但我认为这正是一项要求。 因此、若干个2^n 的缓冲器可能会导致此类问题。

    我将让我们的测试一直运行到明天、如果没有问题发生、我认为我们可以看到问题已经解决。

    此致、

    费利克斯

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

    因此、我想提醒一下、我们可以让它一整夜都能正常运行。 我还修改了另一个项目以使用12个缓冲区、那里也发生了错误。
    所以、缓冲器的数量不是2^n 是一个问题。

    此致

    费利克斯

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

    尊敬的 Felix:

    我明白你的问题. 现在,你可以继续进行2^n 缓冲区分配,给我一些时间,我可以发布我的分析报告。

    此致、

    S.Anil.