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:对多核设置中 ISR 行为的澄清

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

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1508538/tms320f28388d-clarification-on-isr-behavior-in-multi-core-setup

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

工具/软件:

尊敬的团队:

在我的当前实现中、我为每个内核配置了相同的两个中断源:

  • CPU1 :ADC ISR1、eCAP1
  • CPU2 :ADC ISR1、eCAP1

两个内核都由各自的 ISR 触发、但在每个内核上执行的代码不同。 ePWM 模块启动 ADC 转换启动(SOC)、在 ADC 转换完成后、ADCINT1会在两个内核上触发中断。 PWM 计时器周期约为39 µs。

此外、每个线路周期产生一次 eCAP 中断、并且还会触发两个内核。 我使用在每个 ISR 的入口点和出口点切换的 GPIO 捕获了中断波形、并在下面分享了结果。

出于测试目的、我在 eCAP1 ISR 中添加了超出 PWM 中断周期的额外计算。

波形通道映射:

  • CH1:CPU1 ADC ISR
  • CH2:CPU1 eCAP ISR
  • CH3:CPU2 ADC ISR
  • CH4:CPU2 eCAP ISR

通常、ADC ISR 在每个39 µs 期间触发。 但是、如果在 ADC ISR 之前对 eCAP ISR 进行服务、则会延迟 ADC ISR 执行、直到 eCAP ISR 完成。

在一种情况下、eCAP ISR 在下一个 ADC ISR 之前完成、只剩下一个小窗口用于在 CPU1上执行 ADC ISR。

在另一种情况下、eCAP ISR 没有留出足够的时间、从而导致跳过一个 ADC ISR。 我的问题是:在这种情况下、CPU2为什么也跳过了 ADC ISR?

我怀疑 ADC 外设归 CPU1所有、因此它负责清除中断标志。 如果 CPU1没有清除标志、则 ADC ISR 不会再次触发。 但是、一旦 CPU1执行下一个 ADC ISR 并清除标志、CPU2也应该能够响应 ADC 中断。

我的疑问是:

  1. 在这些情况下、哪些情况可能会导致 CPU1和 CPU2同时缺少 ADC ISR?
  2.   考虑到非拥有内核无法清除中断标志、一个内核所拥有的外设(如 ePWM 或 ADC)是否可以在另一个内核上触发 ISR、而不在其自身内核上触发该 ISR?
  3. 鉴于 eCAP ISR 具有最低的优先级、是否有一种 EINT  DINT like below在 eCAP ISR 内使用简单嵌套(使用和启用/禁用中断)来确保始终执行 ADC ISR 的有效方法?

__interrupt void ecap3ISR(void)
{
    if (ECap3Regs.ECFLG.bit.CEVT1 == 0x1)
    {
        ECap3Regs.ECCLR.bit.CEVT1 = 0x1;
    }
    if (ECap3Regs.ECFLG.bit.CEVT2 == 0x1)
    {
        ECap3Regs.ECCLR.bit.CEVT2 = 0x1;
    }
    if (ECap3Regs.ECFLG.bit.CEVT3 == 0x1)
    {
        ECap3Regs.ECCLR.bit.CEVT3 = 0x1;
    }
    if (ECap3Regs.ECFLG.bit.CEVT4 == 0x1)
    {
        ECap3Regs.ECCLR.bit.CEVT4 = 0x1;
    }
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP4;
    ECap3Regs.ECCLR.bit.INT = 0x1;
    
    EINT;
    
    /* some code
    */
    
    DINT;
}

4. 关于中断嵌套的最后一个问题:如果我希望 ePWM 中断抢占 eCAP ISR (来自不同的中断组)、启用该功能的步骤是什么? TI 的文档介绍了同一组内的嵌套、但不涵盖跨组的嵌套。

允许通过 ePWM 嵌套 eCAP 是否存在风险? 我想避免出现类似的情况eCAP1 -> EINT -> eCAP2 -> EINT -> ePWM、其中两个 eCAP ISR 会延迟较高优先级的 ePWM ISR。 我的目标是尽可能地减少延迟、并确保 ePWM 不会因背对背 eCAP 而延迟。

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

    您好:

    很抱歉我延迟了回复。 以下是我对您问题的回答:

    [引述 userid="554226" url="~/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1508538/tms320f28388d-clarification-on-isr-behavior-in-multi-core-setup
    • 在这些情况下、哪些情况可能会导致 CPU1和 CPU2同时缺少 ADC ISR?
    [/报价]

    为了进行澄清、您只清除 CPU1 ISR 上的 ADC 标志、是这样吗? 在这种情况下、我认为这确实解释了您看到的行为。 中断触发最初将从 ADC 同时发送到两个 CPU。 此时、CPU1仍在执行 eCAP ISR、因此在 eCAP ISR 完成之前不会执行 ADC ISR。 请注意、来自同一源的多个中断不会累积、因此、如果到此时前一个触发挂起时从 ADC 发送另一个触发信号、它仍然只执行 ISR 一次(如中的波形所示)。 CPU2能够立即执行 ADC ISR、因此确实如此、但是由于它没有所有权、因此不会清除 ADC 标志。 当下一个触发来自 ADC 时、(0至1)标志没有实际的电平变化、因此 CPU2上的 PIE 不会获得任何中断、并且 CPU1会在该中断被阻止之前执行中断。

    避免这种情况的一种方法是将 ADC 置于连续模式、这样实际上无需在 ISR 中清除该标志即可接收进一步的中断。 这将使用此处的位字段:

    Unknown 说:
      考虑到非拥有内核无法清除中断标志、一个内核所拥有的外设(如 EPWM 或 ADC)是否可以在另一个内核上触发 ISR、而不在其自己的内核上触发 ISR?

    从技术上讲、您可以在 eCAP ISR 内部手动禁用 ADC ISR、并在准备好再次开始在 CPU1上获取中断时在 main 或某处重新启用该 ISR。 对于这种方法、 在使用 IPC 完成 ISR 时、您需要以某种方式让 CPU2信号 CPU1、我不建议使用此方法、因为这会更复杂。

    Unknown 说:
    鉴于 eCAP ISR 具有最低优先级、是否是 EINT  DINT like below在 eCAP ISR 中使用简单嵌套(使用和启用/禁用中断)来确保始终执行 ADC ISR 的有效方法?

    是的、在这种情况下确实有效(假设这些是 CPU1上唯一启用的两个中断)。 只需记住、在使用嵌套 ADC ISR 完成后、CPU1仍需要返回并完成 eCAP ISR 的执行。 由于  ISR 之间的额外分支、会增加一些 ISR 执行时间。 与引入的常规 ADC ISR 相比、嵌套 ADC ISR 在触发和执行之间将具有更多的延迟。

    Unknown 说:
    ]关于中断嵌套的最后一个问题:如果我想让 ePWM 中断抢占 eCAP ISR (来自不同的中断组)、启用该功能的步骤是什么? TI 的文档涵盖了同一组内的嵌套、但不涵盖跨组的嵌套。

    我建议查看我对此主题的第一次回复: (+) TMS320F28388D:EINT 和嵌套- C2000微控制器论坛- C2000Tm︎ 微控制器- TI E2E 支持论坛。 它介绍了如何实现所有不同的嵌套场景。 您特别要问的一个问题将位于"简单嵌套"案例1下。

    Unknown 说:
    允许 ePWM 嵌套 eCAP 是否存在风险? 我想避免出现类似的情况eCAP1 -> EINT -> eCAP2 -> EINT -> ePWM、其中两个 eCAP ISR 会延迟较高优先级的 ePWM ISR。 我的目标是尽可能地减少延迟、并确保 ePWM 不会被背对背 eCAP 延迟。[/报价]

    这种情况仍然是"简单嵌套"的情况1。 通过这种实现、您概述的序列不会发生、因为 eCAP 中断组仍会打开 ACK;eCAP ISR 将无法相互嵌套。 只有 ePWM 中断才能嵌套在较低组优先级 eCAP ISR 内。

    如果此回答对您有帮助Slight smile请支持此回答

    此致、

    Delaney

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    感谢您的详细说明。 请确认以下几点:
    来自同一源的多个中断不会排队–如果在前一个 ADC 中断仍在执行时触发了 ADC 中断、则会跳过新的中断? 此外、如果我清除 ADC 中断标志并在 ISR 开始时确认它、这是否会终止当前中断并允许新触发的中断运行?
    2.还有一个观察结果:我同时有 ePWM 和 ADC 中断、并且已将 ePWM 所有权分配给 CPU2。 使用仿真器时、如果我首先启动 CPU1、然后启动 CPU2、则 ePWM 中断不会运行。 中断似乎会进入 CPU1 (它不是所有者)、并且永远不会到达 CPU2。 但是、如果我先启动 CPU2、然后再启动 CPU1、则 ePWM 中断会在两个 CPU 上按预期运行。
    使用仿真器时、CPU 的启动顺序是否存在任何依赖性? 上下文中、在 CPU1的 main ()函数中、我首先配置 ePWM 模块、然后在 IPC 同步后、将所有权分配给 CPU2。 我从 CPU1引导 CPU2、然后才会将 ePWM 模块重新分配给 CPU2。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Yeidda:

    1.此处的更正:如果在为前一个 ISR 提供服务时出现中断、则可以从 PIE 中的同一位置获得另一个中断。 由于只有一个闩锁、您将无法维修多个闩锁。 我之前提到的是、如果在前一个 ADC ISR 仍处于挂起状态而不是在其执行期间获得第二个 ADC ISR、则会丢失中断。 我要讨论的情况是、当 eCAP ISR 正在执行时、您是否收到两个 ADC ISR。 在这种情况下、在 eCAP ISR 之后只执行一个命令。  

    在硬件中思考这一点有助于:PIE 一次只能向 CPU 发送一个中断、并且每个中断的电路中只有一个 PIEIFR 锁存器。 进入 ISR 时、INTM 开关断开、当前组的 ACK 断开。 如果在此期间来自外设的多个中断进入、则这些 PIEIFR 的锁存器仍将为高电平(PIE 中没有指示进入的数量)。

    如果您只清除 ADC 标志并关闭组 ACK、INTM 位仍将阻止进一步的中断、因为这会在每次执行 ISR 时自动打开。 我只会建议在我提供的链接的其中一种场景中遵循代码、任何其他代码都可能导致意外行为。  

    2.当您说"已开始使用仿真器"时、您指的是连接到目标、加载.out 或在 CCS 中按 Resume。 您能否概述一下针对两个内核执行这些步骤中每个步骤的顺序? 我们提供了一些有关如何完成此操作的文档:  6.调试多核— 《C2000Tm多核开发指南》。  

    此致、

    Delaney

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    感谢您的详细说明。
    2.我使用 XDS220仿真器连接到 DSP、并遵循了链接中提供的过程。 我的问题是:当我更改 ePWM 内核分配和 CPU2引导顺序时、这似乎会影响该行为。 首次进入 CPU1而不是 CPU2的中断、因此中断标志不会被清除。 因此、不会再触发 EPWM 中断。 但是,当我更改顺序时,代码会按预期工作—首先将内核分配给 EPWM ,然后引导 CPU2和一些中间语句。  
    您能否澄清一下、我们是否需要针对 CPU2引导、内核分配、IPC 同步和中断启用的顺序采取任何预防措施?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    最新观察:

    ePWM 配置在 CPU1中完成、稍后将所有权分配给 CPU2。

    我通过 xds220仿真器连接到28388D。 我已连接到 CPU1、加载 OUT 文件、然后连接到 CPU2并加载 OUT 文件。 两个 CPU 都处于支持模式、如下图所示。

    首先使用突出显示的按钮运行 CPU2、

    CPU1中的代码

    void main(void)
    {
        //
        // Initializes device clock and peripherals
        //
        Device_init();
    
         InitGPIO();
    
        //
        // Initialize PIE and clear PIE registers. Disables CPU interrupts.
        //
        Interrupt_initModule();
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        //
        Interrupt_initVectorTable();
    
         //
        // Give memory access to GS0 and GS7 RAM to CPU1
        //
        MemCfg_setGSRAMControllerSel((MEMCFG_SECT_GS0 | MEMCFG_SECT_GS1 |
                                      MEMCFG_SECT_GS2 | MEMCFG_SECT_GS3 |
                                      MEMCFG_SECT_GS4 | MEMCFG_SECT_GS5 |
                                      MEMCFG_SECT_GS6 | MEMCFG_SECT_GS7),
                                 MEMCFG_GSRAMCONTROLLER_CPU1);
    
        //
        // Give memory access to GS8 to GS15 RAM to CPU2
        //
        MemCfg_setGSRAMControllerSel((MEMCFG_SECT_GS8 | MEMCFG_SECT_GS9 |
                                      MEMCFG_SECT_GS10 | MEMCFG_SECT_GS11 |
                                      MEMCFG_SECT_GS12 | MEMCFG_SECT_GS13 |
                                      MEMCFG_SECT_GS14 | MEMCFG_SECT_GS15),
                                 MEMCFG_GSRAMCONTROLLER_CPU2);
    
        initADC();
    
        initEpwm(&EPwm6Regs);
    
        EPwm6Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO;     // Select INT on Zero event
        EPwm6Regs.ETSEL.bit.INTSELCMP = 0;
        EPwm6Regs.ETSEL.bit.INTEN = 1;                // Enable INT
        EPwm6Regs.ETPS.bit.INTPRD = ET_1ST;           // Generate INT on 3rd event
    
        EPwm6Regs.ETSEL.bit.SOCASEL = ET_CTR_ZERO;
        EPwm6Regs.ETSEL.bit.SOCASELCMP = 0;
        EPwm6Regs.ETPS.bit.SOCAPRD = ET_1ST;
        EPwm6Regs.ETSEL.bit.SOCAEN = 1;
    
    
    	//ecap & Other epwm intializations
    
    	// CAN & I2C initialization
    
    
        initVariables();
    
                             //
        // Hand-over the CAN module access to CPU2
        //
        SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL0_EPWM, 5, SYSCTL_CPUSEL_CPU2);
        SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL0_EPWM, 6, SYSCTL_CPUSEL_CPU2);
        SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL0_EPWM, 7, SYSCTL_CPUSEL_CPU2);
        SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL0_EPWM, 8, SYSCTL_CPUSEL_CPU2);
        SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL11_ADC, 1, SYSCTL_CPUSEL_CPU2);
    
        //
        // Boot CPU2 core
        //
        #ifdef _FLASH
            Device_bootCPU2(BOOTMODE_BOOT_TO_FLASH_SECTOR0);
        #else
            Device_bootCPU2(BOOTMODE_BOOT_TO_M0RAM);
        #endif
    
    
    //       Flgs.Flt_Flgs1.Node_ID_Initalization_Fail_Flg = FALSE;
    
        InitCLA();
    
        // Clear any IPC flags if set already
        //
        IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);
    
        //
        // Synchronize both the cores.
        //
        IPC_sync(IPC_CPU1_L_CPU2_R, IPC_FLAG31);
    
        Interrupt_register(INT_EPWM6, &epwm6ISR);
        Interrupt_register(INT_ECAP1, &ecap1ISR);
        Interrupt_register(INT_ECAP2, &ecap2ISR);
        Interrupt_register(INT_ECAP3, &ecap3ISR);
        Interrupt_register(INT_ECAP4, &ecap4ISR);
    
        Interrupt_enable(INT_EPWM6);
        Interrupt_enable(INT_ECAP1);
        Interrupt_enable(INT_ECAP2);
        Interrupt_enable(INT_ECAP3);
        Interrupt_enable(INT_ECAP4);
    
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        //
        EINT;
        ERTM;
    
    	//while(1) function
    }
    

    CPU2中的代码

    void main(void)
    {
        //
        // Initialize device clock and peripherals
        //
        Device_init();
    
    
        //
        // Initialize PIE and clear PIE registers. Disables CPU interrupsampleTime.
        //
        Interrupt_initModule();
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        //
        Interrupt_initVectorTable();
    
    
        //
        // Clear any IPC flags if set already
        //
        IPC_clearFlagLtoR(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);
    
        //
        // Synchronize both the cores.
        //
        IPC_sync(IPC_CPU2_L_CPU1_R, IPC_FLAG31);
    
     //   EPwm6Regs.ETCLR.bit.INT = 1;
        //
        // Interrupts that are used in this example are re-mapped to
        // ISR functions found within this file.
        //
        Interrupt_register(INT_EPWM6, &epwm6ISR);
        Interrupt_register(INT_ADCA1, &adcAISR);
    
         //
        // Enable EPWM1 Interrupt
        //
        Interrupt_enable(INT_EPWM6);
        Interrupt_enable(INT_ADCA1);
        //
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        //
        EINT;
        ERTM;
        //
        // Loop forever. Wait for IPC interrupt
        //
        while(1)
        {
            scheduleEvents();
        };
    }

    用例1:  如果我先运行 CPU2、然后再运行 CPU1、则 pwm6 ISR (它在两个 CPU 中均输入)没有问题、因为它会进入 CPU2和 EPwm6Regs.ETFLG.bit.INT 被清除、从而使其具有基于重复的 ePWM TBPRD。  

    用例2:  如果我先运行 CPU1、然后再运行 CPU2、则 pwm6 ISR 将在 CPU1中进入、但不会进入 CPU2   、因为 EPwm6Regs.ETFLG.bit.INT 未被清除。 因此、任何 CPU 中都不会生成 EPWM 中断。

    我在下面观察到了对两个 CPU 中取消中断的修改。

    如果我在 IPCsync 之后添加以下语句、然后在 CPU2中启用中断、则没有问题、两个 CPU 均根据需要执行中断。
    "EPwm6Regs.ETCLR.bit.INT = 1;"  

    如果在 CPU1主函数中调整 CPU2内核和 CPU2引导的 ePWM6分配位置、则也会正常工作。
     我找到了权变措施、但我不明白为什么会发生这种情况、您能解释一下为什么在我从 CPU2开始使用 CPU2时、中断不在外壳上运行以及在启动 CPU2时起作用?

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

    您好:

    Delaney 目前不在办公室。 请预计回复将延迟、直至她下周返回。  

    此致、

    Allison

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

    尊敬的 Allison:

    感谢您的更新。

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

    尊敬的 Yeidda:

    听起来您看到的只是基于系统的不同时序、这是由于您使用的代码版本而导致的。 由于实际上只有一个内核可以清除标志来导致未来的中断、因此您需要确保两个内核上的 ISR 操作是同步的。

    请注意、根据 TRM、对于 ETFLG 中断、 "N o 将生成更多中断、直到标志位清零。 在 ETFLG[INT]位仍设置时、最多可以挂起一个中断。  如果有一个中断挂起、在清除 ETFLG[INT]位之前不会生成该中断 "

    您能否看看 此处链接的线程 、该线程具有类似的实现并同步两个 ISR 的运行?

    此致、

    Delaney

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

    尊敬的 Delaney:

    感谢您的更新。

    链接信息不能解决我的问题。

    您能解释为什么案例2不工作、但案例1正常吗? 为什么引导顺序会创建依赖关系?

    您认为代码的版本会导致这种情况的原因是什么?  

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

    尊敬的 Yeidda:

    很抱歉耽误你的时间。  两个内核的同步似乎有些问题。  

    ISR 正在 CPU1中进入、但由于 EPwm6Regs.ETFLG.bit.INT 未 被清除、因此未进入 CPU2

    我认为案例2不起作用、因为如果 CPU1首先开始运行、它将在 CPU2启用 ISR 之前初始化外设、启用中断并进入 ISR。  如果在 CPU2运行之前在 CPU2上触发了 ISR、则不会转至 ISR、也不会像您所说的那样清除标志。  

    通过"版本的代码"我只是指这个陈述:

    如果我在 IPCsync 之后添加以下语句、然后在 CPU2中启用中断、则没有问题、则两个 CPU 均会根据需要执行中断。
    "EPwm6Regs.ETCLR.bit.INT = 1;"
     

    此版本的代码将确保在 CPU2开始运行并启用中断后、在 CPU2上明确触发中断。 这将清除 ISR 结束时的标志并支持进一步的中断。

    是否可以尝试移动 IPC_SYNC ();调用两个初始化代码(对于 CPU1和 CPU2 )都在 EINT 之前? 这应确保内核将获得第一个中断并完全同时分支到 ISR。

    此致、

    Delaney

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

    我以前试过、结果还是成功的。 谢谢