c - STM32 VCP驱动——只有优化后指针才会失效

标签 c optimization usb stack-overflow stm32

我正在使用 STM32F405 微 Controller 开发一个嵌入式项目,并且有一些非常令人困惑的行为。我正在将现有(工作)项目从 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. 如果没有优化和有优化存在差异,那么这听起来与优化+ volatile 问题有关。好想就是懂volatile类型限定符及其与 C 优化的关系。网上有很多好的文章。
  2. 栈只是存在,你不能“分配”更多的栈(至少在嵌入式系统上不是这样)。该函数可以“分配”堆栈,在这种方式下,它使用堆栈来存储局部变量、寄存器状态和移动堆栈指针。当 IRQ 发生时,当前执行状态被保存在栈顶,然后 IRQ 处理函数被执行。您可以通过在访问堆栈末尾的内存地址时设置断点来检测是否发生堆栈溢出。在 STM32 上,当您将某些内容放入堆栈时,堆栈指针会减少。您可以使用 -fstack-usage 检查函数的堆栈使用情况。但这与问题无关。使用更好的优化进行编译会创建堆栈使用量更小的代码。
  3. 我猜所有/任何。

现在我不知道你所说的 是什么意思,实际的错误表现为一个指针 (GREGS) 被取消引用,即使该指针之前已在同一函数中使用过。。程序员的意图是取消引用 GINTMSK 两次。 GINTMSK 指针被声明为 volatile,每次使用时都会取消引用并且不会优化掉。这也是我们的意图,因为 GINTMSK 是一个硬件映射寄存器变量。
根据您的描述,pdev->regs.GREG 的值似乎在该开关之间的某处被修改。
USB_OTG_ReadPacket()看起来相当简单,但也许缓冲区指向错误的位置并且它覆盖了 pdev 结构?
也许在此中断期间,其他具有不同优先级的中断会启动并修改 pdev 结构。尝试添加 __disable_irq()__enable_irq() 守卫。
如果你正在移植这个项目,你可以考虑转移到 STM32 HAL 库并使用 STM32CubeMX 程序生成一些代码。 STM32 库在每个版本中都变得更好,一些最古老的库在各种优化方面存在问题。

关于c - STM32 VCP驱动——只有优化后指针才会失效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50311851/

相关文章:

c# - 如何在C#中用指针调用C语言编码的dll

c# - 禁用特定函数或代码块的编译器优化 (C#)

mysql - 如何优化mysql中的地理定位数据库以进行欧几里德距离搜索/查询

usb - 使用STM32 USB设备库将闪存作为大容量存储设备

c# - 过早中止 BeginRead 和 BeginWrite 的正确方法?

c - 使用静态库而不是共享库

c - 使用 Xor 交换产生错误结果

c# - 使用 C# 通过 “USB Virtual Serial Port” 与 USB 设备通信?

c++ - 通过 tcp 传输的文件末尾的额外换行符

java - 需要优化帮助 - 在 java 中生成幻方