STM32 VCP驱动程序 - 仅在优化时指针变为无效

时间:2018-05-13 00:58:56

标签: c optimization usb stack-overflow stm32

我正在使用STM32F405微控制器进行嵌入式项目,并且有一些非常令人困惑的行为。我正在将现有(工作)项目从STM32F1移植到STM32F4,但我已为VCP添加了ST的标准外设库USB堆栈。

如果我使用-O0优化编译程序,那么它会无限期地按预期运行。但是,如果我使用-O2进行编译,那么项目将运行10-15分钟,但随后我会看到ST的VCP驱动程序代码中出现堆栈溢出的情况。

实际的错误表现为指针(GREGS)变为无效,即使指针早先在同一个函数中使用过。该指针指向USB外设的硬件中断配置寄存器,因此实际数据没有消失,但是当访问指针时,我得到一个错误,我可以看到我的调试器指针无效。 (我已经从下面的usb_dcd_int.c复制了实际的函数,指出了麻烦的行。)

static uint32_t DCD_HandleRxStatusQueueLevel_ISR(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_GINTMSK_TypeDef  int_mask;
    USB_OTG_DRXSTS_TypeDef   status;
    USB_OTG_EP *ep;

    /* Disable the Rx Status Queue Level interrupt */
    int_mask.d32 = 0;
    int_mask.b.rxstsqlvl = 1;
    /*****************************************************************/
    /*********** POINTER IS READ HERE - NO PROBLEMS ******************/
    /*****************************************************************/
    USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, int_mask.d32, 0);

    /* Get the Status from the top of the FIFO */
    status.d32 = USB_OTG_READ_REG32( &pdev->regs.GREGS->GRXSTSP );

    ep = &pdev->dev.out_ep[status.b.epnum];

    switch (status.b.pktsts)
    {
    case STS_GOUT_NAK:
        break;
    case STS_DATA_UPDT:
        if (status.b.bcnt)
        {
          USB_OTG_ReadPacket(pdev,ep->xfer_buff, status.b.bcnt);
          ep->xfer_buff += status.b.bcnt;
          ep->xfer_count += status.b.bcnt;
        }
        break;
    case STS_XFER_COMP:
        break;
    case STS_SETUP_COMP:
        break;
    case STS_SETUP_UPDT:
        /* Copy the setup packet received in FIFO into the setup buffer in RAM */
        USB_OTG_ReadPacket(pdev , pdev->dev.setup_packet, 8);
        ep->xfer_count += status.b.bcnt;
        break;
    default:
        break;
    }

    /* Enable the Rx Status Queue Level interrupt */
    /*****************************************************************/
    /************************* GREGS == :-(   ************************/
    /*****************************************************************/
    USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, 0, int_mask.d32);

    return 1;
}

我使用vanilla GNU makegcc-arm-none-eabi 5-4-2016q3作为我的工具,ST的vanilla链接器和启动脚本从2015年开始用于STM32F405,VCP代码来自2012年3月。我是新手启动和链接器脚本,但我不能在任何一个中看到任何可疑的东西。我也没有看到ST的VCP代码中有任何明显的东西,但我当然不理解每一行。

我有三个问题:

  1. 这听起来像堆栈溢出吗?
  2. 如何分配IRQ的堆栈?在ST的实现中,VCP中断有一个非常深的调用树。我只需要为VCP IRQ分配更多吗?
  3. -O2中哪些优化可能会导致此行为?我想知道我是否可以选择性地禁用一些可能有助于我追踪我的错误的优化。

1 个答案:

答案 0 :(得分:0)

  1. 如果没有优化和优化存在差异,则此声音与优化+易失性问题相关。好的想法是了解volatile类型限定符以及它与C优化的关系。网上有很多好文章。
  2. 筹码存在,你不能分配"更多堆栈(至少在某种意义上不适用于嵌入式系统)。该功能可以"分配"堆栈,顺便说一下,它使用堆栈存储局部变量,注册状态并移动堆栈指针。当IRQ发生时,当前执行状态保存在堆栈顶部,然后执行IRQ处理程序函数。您可以通过在访问作为堆栈末尾的内存地址时设置断点来检测是否发生堆栈溢出。在STM32上,当你把东西放在堆栈上时,堆栈指针会减少。您可以使用-fstack-usage检查函数的堆栈使用情况。但这与问题无关。使用更好的优化进行编译会创建具有较小堆栈使用的代
  3. 我想全部/任何。
  4. 现在我不知道The actual bug manifests as a pointer (GREGS) getting dereferenced even though the pointer had been used earlier in the same function.你是什么意思。程序员的意图是两次取消引用GINTMSK。 GINTMSK指针被声明为volatile,它将在每次使用时被解除声明并且不会被优化掉。这也是意图,因为GINTMSK是硬件映射的寄存器变量 根据您的描述,看起来pdev->regs.GREG的值在某个位置被修改了。
    USB_OTG_ReadPacket()看起来相当简单,但是缓冲点可能位于错误的位置并覆盖了pdev结构?
    也许在此中断期间,具有不同优先级的其他中断将触发并修改pdev结构。尝试添加__disable_irq()__enable_irq()警卫 如果要移植此项目,可以考虑移动到STM32 HAL库并使用STM32CubeMX程序生成一些代码。每个版本的STM32库都会变得更好,而一些最老的库在各种优化方面都存在问题。