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.

[参考译文] MCU-PLUS-SDK - AM243X:使用来自多个内核的外设

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1383789/mcu-plus-sdk-am243x-using-peripherals-from-multiple-cores

器件型号:MCU-PLUS-SDK AM243X

工具与软件:

您好!

我们使用的是 MCU PLUS SDK 09.01 tiarmclang LTS 3.2.0。 我们通过自己的通信协议的附加层使用 IPC。 目前、我们在一个集群中使用两个内核、但也可以使用全部4个内核。 另外请注意、对于以下内容、我们不使用 SYSCFG、但我们修改了 SDK-API、这样、驱动程序将不会获取数组的索引、但可以直接将 Config 用作指针参数。 因此,变化很小,只与 open()函数相关。

一般来说、我们有很多方法可以修改驱动程序设置。 例如、在 SpiDriver 等的书面构造函数设置后、模块的时钟立即激活、并设置正确的中断等。 一般来说、这种方法已经效果很好。

我们现在可以将我们的软件组件从一个内核切换到另一个内核、但由于我们(目前)未实现任何用于外设访问的特殊处理程序、因此我们需要从两个内核访问外设。

因此、在我们的示例中、假设设置如下:

一个集群中的2个内核(mcu0_0和 mcu0_1)都在相同 MCSPI 模块但不同的通道上访问 SpiDriver。

我们对阻止并发访问的想法是使用自旋锁。 所以每个驱动程序都有自己的硬编码 spinlock-id。 有一些限制、但我稍后会在 PS 中处理这些限制。

通常、一旦一个驱动程序访问 SPI 驱动程序、我们就锁定自旋锁。 无论它是任何打开、关闭、收发或任何其他-旋钮锁被锁定。 我们当前的用例是在一个 SPI 通道上的移位寄存器处安装一些 LED、并在另一个 SPI 通道上安装一个 EEPROM (我们使用我们自己的驱动器、而不是 SDK 的驱动器)。

有意思的是、这似乎通过 CCS 的加载实现了完美的工作。 可以从这两个内核访问 SPI。

但如果从闪存引导、它将无法正常工作。 当我们尝试从 SciClient 写入寄存器时、出现数据中止(sciclientobj.c 中的 CSL_REG32_WR_RAW)。很遗憾、中止处理程序不提供任何有用的信息。 但 CP15寄存器显示:0x1808、所以在写入时包含一个外部中止。 数据故障地址为0x4D001004。 因此、根据 TRM、它是 DMASS0_SEC_PROVISION_SRC_TARGET_DATA 的一部分。

我们无法真正调试这种情况、因此在这里可能需要您的帮助。 这似乎是某种种族条件,行为真的很奇怪. 我们在一个内核上实现了超过10秒的睡眠、以排除同时激活某些东西的可能性、但这并没有阻止它。 有趣的是、当我们在开始时添加一个 loop_forever (例如在引导加载程序调试中)、然后让其随后运行时、它会起作用。

为了更详细地解释启动(每个内核),因为我们不使用 SYSCFG,请参阅从 main()运行:

1. Hwip_init()
2、设置时钟(内核0_0的 HWTimer 8和内核0_1的 HWTimer 9,设置中断正确,调用 ClockP_init ()))
3. Hwip_open()
4. Sciclient_init (CoreID)
5.设置 IPC、等待同步
6.设置 SpiDrivers:
6.1设置 SPI 模块的中断、自旋锁 ID 和使能时钟(根据使用的 SPI 模块)
6.2打开 SpiDriver
6.3调用 MCSPI_chConfig (当前针对每个内核上的每个通道)

所以,由于错误发生在防火墙范围内的某个地方,我不知道该怎么做。 这与 boardcfg 相关吗? 但是、为什么在通过 CCS 加载或开始使用 while 循环的引导加载程序-调试-方法时、它能够起作用呢?

PS:

是的、我们知道自旋锁可能不是一个好主意。 TRM 指出执行锁定的时间不应超过200ns、操作应短等 理论上存在的问题是:一个内核的任务优先级低、用于访问 SPI、将其锁定、而另一个内核的任务具有更高的优先级、当尝试访问同一外设以及尝试在 while 循环中获取自旋锁时、可能会被阻止、从而相当长地阻止一个内核。 此外、如果在同一个内核上有多个实体想要访问同一个 SPI 模块、如果一个较低优先级任务取代了自旋锁、而一个较高优先级任务-也使用了 SPI 模块-取代了这个任务、那么他们自身就会死锁。 但不应发生最后一种情况、因为我们还使用信标确实会阻止从一个内核进行并发访问。 自旋锁随后直接锁定(当然也会释放)。
为了至少减小影响、我们为每次自旋锁检查设置了超时和睡眠(1)、以便确保较低的优先任务可以继续运行。 我们稍后可能会细化此行为。

我们现在可以顺利完成调试、因为至少我们可以保证不会同时访问 SPI 模块。

您是否知道我们可以在这里做些什么来运行这个程序?

此致

Felix

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

    Felix、您好!

    很抱歉,因为我昨天离开了,所以我的答复被推迟了。

    您可能会在一天或两天内得到回复。

    此致、

    Anil。

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

    Felix、您好!

    因此、您通过两条 CS 线路连接一个 MCSPI 瞬间。

    一个 CS 连接到移位寄存器、另一个 CS 连接到 EEPROM。

    一个 CS 在一个 R5F0_0内核中控制、另一条 CS 线路在 R5F0_1中控制。

    请确认您使用的是两个通道的 DMA 吗?

    我们在一个内核上实施了休眠超过10秒、以排除同时激活某些东西的可能性、但这并没有阻止它。

    最初、我的怀疑是两个内核同时从闪存模式访问 DMA。

    因为它已经通过 CCS 测试并可以正常运行。 在 CCS 中、初始化一个内核中的所有外设与另一个内核之间肯定存在差距。 因此、此问题可能在 CCS 加载中缺失。

    在一个应用程序中添加 while (1)条件后、您再观察到一个内核已完成初始化、因此不会出现此问题。

    在另一个内核中、我们使用 while (可变)条件进行初始化。 因此,在初始化一个内核与另一个内核之间存在一些差距。

    实际上、MCSPI 上有5个模块可用。 那么为什么我们不能为 EEPROM 和移位寄存器连接一个专用通道呢?

    如果一个内核正在访问 mcspi、等待一个内核完成、并在一个内核完成后、将同一通道用于具有 IPC 反馈的不同内核、则可以添加任何反馈机制。

    我假设为了控制移位寄存器,我们不需要启用 RX 模式。

    我希望两个 MCSPI 通道配置相同、但 CS 线路除外。

    由于错误发生在防火墙范围内的某个位置、我不知道该怎么办。 这与 boardcfg 相关吗? 但是、为什么在通过 CCS 加载或使用开头使用 while 循环的引导加载程序调试方法时、它才起作用?[/QUOT]

    大多数情况下这不是防火墙的问题,因为你已经确认代码在 CCS 中工作正常.

    所以,我希望你不使用没有引导模式在上述情况 .

    您能否确认您使用的是哪种引导模式?

    无引导模式?  

    如果您怀疑防火墙存在问题、则所有防火墙例外都会路由到 SYSFW 核心。

    因此、请借助下面的常见问题解答启用 sysfw 日志并共享日志。

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1377336/faq-am64x-am243-how-to-enable-sysfw-trace-on-am64x-am243-devices

    此致、

    Anil。

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

    您好、Swargam、

    我看到我漏掉了一些信息。

    我们没有使用任何 DMA。 我们无法连接的方式有所不同、因为我们已连接所有引脚、并且它们共享其他工作。 是的、两个通道的配置相同。

    任何情况下均采用 xSPI 引导模式。 连接时也是如此。

    我们可以放弃这种防火墙问题、因为我认为我们至少找到了一种同时有效的解决方案。

    所以我们现在使用了自旋锁。 一旦 SPI 被访问、它就被直接锁定。 它的工作原理是这样。 我们认为上一个问题是因为调用 open()函数而忘记了自旋锁。

    现在、由于自旋锁的作用、每个内核都具有专用访问权限、而无需任何其他内核同时访问。 那么、我会看到这正常工作。

    这里唯一的问题是:这是合法的吗? 或者你会说:"不,不是那么好,也许有一个更好的解决方案。" ? 因为我们现在正在考虑使用每个(我们的)驱动程序的专用 ID 来实现这个自旋锁。 这很容易实现、因为我们还有一个 TI 驱动程序包装器。 可以选择在使用多个内核时激活、否则不会在中编译。

    这意味着还会有用于 UART、OSPI (我们不使用 XIP、所以从这个视图中不应该是问题)、μ I²C 等的自旋锁。 至少在我的理解下,这样我们就可以保证无并发地访问外围设备,缺点是每个核心的任务可能优先级颠倒。 另一方面、我们无论如何都会使用二进制信标、即使较高任务想要访问资源、它也需要等待、直到较低的优先级任务完成。 因此内核更多的情况会稍微复杂一些。

    此致

    Felix

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    这里唯一的问题是:这样做是否合法? 或者你会说:"不,不是那么好,也许有一个更好的解决方案。" ? 因为我们现在正在考虑使用每个(我们的)驱动程序的专用 ID 来实现这个自旋锁。 这很容易实现、因为我们还有一个 TI 驱动程序包装器。 可选择在使用多个内核时激活、否则不会在[/QUOT]中编译

    Felix、您好!

    我可以使用自旋锁、但我担心中断处理。

    在我的分析中、我发现如果一个内核被触发 TX 和 RX 中断、那么相同的中断会进入两个内核。

    我不知道您是如何处理 ISR 中断的?

    您能否共享每个内核的应用程序或 IRQ 句柄? 我可以尝试检查并提供反馈。

    我的建议是始终使用一个内核中的外设,而不与其他内核共享它.

    您的应用程序可以通过以下方法实现。

    我假设 CS0连接到移位寄存器、CS1连接到 EEPROM。

    因此、R5F0_0根据 R5F0_0应用程序数据控制移位寄存器、一旦完成此操作、下一个请求通过 IPC 从其他内核获取数据、并在获取数据并仅从 R5F0_0应用程序控制 CS1后、请求获取数据。

    如果您认为 EEPROM 数据更多的是写入或读取 EEPROM 存储器上的数据,并且需要更多时间通过 IPC 读取数据。

    然后在 R5F0_1上移动外设控制、可以从这里获取移位寄存器的数据。 我确信,控制移位寄存器主要需要几个字节,而不需要大量数据。

    此致、

    Anil。

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

    嗨、Anil、

    根据我所见、我想我明白、当我们想要手动触发中断时、我们也讨论了该主题、因为该主题也适用于所有内核。

    据我所知、MCSPI 的中断是由 SDK 驱动程序本身处理的。 它使用信标等待中断、该中断属于 MCSPI_OBJECT 的一部分:

        /**< Transfer Sync Sempahore - to sync between transfer completion ISR
         *   and task */
        SemaphoreP_Object       transferSemObj;

    我尚未检查一个内核发生意外中断时会发生什么情况。 但我认为这不是问题、因为来自程序流:如果之前任务没有接收信标、如果稍后发布信标、也应该没问题。 至少我希望是这样吗?

    让我们检查当前仍使用的 MCSPI_peripheralIsr 的 sdk-driver-implementation (did migrate the LLD of the latest SDK (!)):

        int32_t             status = SystemP_SUCCESS;
        uint32_t            transferStatus;
        MCSPI_Config       *config;
        MCSPI_Object       *obj;
        const MCSPI_Attrs  *attrs;
        MCSPI_ChObject     *chObj;
        MCSPI_Transaction  *transaction;
        uint32_t            baseAddr, chNum;
    
        /* Check parameters */
        if(NULL == args)
        {
            status = SystemP_FAILURE;
        }
    
        if(SystemP_SUCCESS == status)
        {
            config = (MCSPI_Config *) args;
            obj = config->object;
            attrs = config->attrs;
            DebugP_assert(NULL != obj);
            DebugP_assert(NULL != config->attrs);
    
            transaction = obj->currTransaction;
            baseAddr = obj->baseAddr;
            if (transaction != NULL)
            {
                chNum = transaction->channel;
                chObj = &obj->chObj[chNum];
                transferStatus = MCSPI_continuePeripheralTxRx(obj, chObj, transaction);
                if ((MCSPI_TRANSFER_COMPLETED == transferStatus) ||
                        (MCSPI_TRANSFER_CANCELLED == transferStatus))
                {
                    /* Process the transfer completion. */
                    /* Stop MCSPI Channel */
                    MCSPI_stop(obj, attrs, chObj, chNum);
    
                    /* Disable TX and RX FIFO */
                    chObj->chConfRegVal &= ~(CSL_MCSPI_CH0CONF_FFEW_MASK | CSL_MCSPI_CH0CONF_FFER_MASK);
                    CSL_REG32_WR(baseAddr + MCSPI_CHCONF(chObj->chCfg.chNum), chObj->chConfRegVal);
    
                    /* Update the driver internal status. */
                    /* transfer completed */
                    transaction->status  = transferStatus;
                    /* Return the actual number of words transferred */
                    obj->currTransaction->count = chObj->curRxWords;
                    if (MCSPI_TR_MODE_TX_ONLY == chObj->chCfg.trMode)
                    {
                        obj->currTransaction->count = chObj->curTxWords;
                    }
                    obj->currTransaction = NULL;
    
                    /*
                    * Post transfer Sem in case of bloacking transfer.
                    * Call the callback function in case of Callback mode.
                    */
                    if (obj->openPrms.transferMode == MCSPI_TRANSFER_MODE_BLOCKING)
                    {
                        SemaphoreP_post(&obj->transferSemObj);
                    }
                    else
                    {
                        obj->openPrms.transferCallbackFxn((MCSPI_Handle) config, transaction);
                    }
                }
                /*
                * Else the transfer is still pending.
                * Do nothing, wait for next interrupt.
                */
            }
            else
            {
                /* There is no ongoing transfer. Disable and clear all interrupts. */
                CSL_REG32_WR(baseAddr + CSL_MCSPI_IRQENABLE, 0U);
                MCSPI_clearAllIrqStatus(baseAddr);
            }
        }
        return;

    因此、如果 args 或事务为 NULL、那么它将不会继续。 如果发生中断、但当前内核未使用 MCSPI、则至少事务应该为 NULL、并且不应修改该内核上的任何数据、中断也会退出。

    打印 MCSPI 的配置可能有用:

    const MCSPI_Attrs spi0Attr = {
        CSL_MCSPI0_CFG_BASE,
        50000000U,
        0, // is set by driver
        MCSPI_OPER_MODE_INTERRUPT,
        8U,
        MCSPI_CH_MODE_MULTI,
        MCSPI_PINMODE_4PIN,
        MCSPI_INITDLY_0
    };

    请记住、我们使用修改后的 SDK-API、驱动程序的包装器也会定义所用 MCSPI 的硬件中断号(当然根据 TRM)。

    因此、MCSPI_OPER_MODE_INTERRUPT 始终在 SDK 驱动器的中断中结束、在中断中布置这个信号量。 我们的应用程序代码"只是"调用读写、之前锁定了自旋锁。

    我们也认为仅使用一个内核来访问外设的解决方案十分复杂、因为我们现在需要添加处理程序、可以将其作为驱动程序。 这是由于我们的设计、您可以将一个 SW-Component 切换到另一个内核、同时它仍保留另一个内核的组件的所有连接(我们在 IPC 上面实现了另一层、该层像正常的函数调用一样工作、并在启动时通过所有内核连接组件)。 但这对于驱动器不起作用。 它们只是实现的简单 C++接口(以及在内部访问 SDK 驱动程序)。 这最终将意味着需要更多的代码和更大的数据空间。
    此外、对于大量数据、传输时间会增加、并且在访问 EEPROM 时、传输时间会使事情变得更复杂。

    我看到我们对该主题也表达了同样的想法。

    我想我们需要利用 GTC 进行一些测量、以便查看与之相比、它需要多长时间(并阻止任务)。 一次通过 IPC 访问、一次通过多个内核共享外设访问。 然后将其与存储器占用空间相关联。 遗憾的是、目前我们没有时间这么做。

    但是、如果我现在已经掌握了所有内核的全部信息、这主要是关于处理 IRQ 的主题、所有内核都能正确接收到? 我认为这应该起作用。 这是一个不必要的中断、但它不应破坏任何东西。

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

    Felix、您好!

    在我的分析中、 我只看到了与中断处理相关的问题、如果已经实施了信标、那么我现在就看不到任何问题。

    正如您所提到的、即使内核每次都可能产生不必要的 中断  、除非 没有问题。

    除了旋转锁,还有我上面分享的另一种方法,访问 EEPROM 应用 内核上的 SPI ,并通过 IPC 或 DMA 读取数据 ,以控制移位寄存器从其他内核 .

    除这两种方法外、不可能在两个内核中使用同一个外设。

    此致、

    Anil

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

    谢谢 Anil、

    我们现在将使用这些选项中的任何一个(当然这由我们自己负责)、但我们可能会考虑通过 IPC 实现更好的解决方案。 到目前为止、谢谢!