STM32 VCP驱动——指针失效只有优化
Posted
技术标签:
【中文标题】STM32 VCP驱动——指针失效只有优化【英文标题】:STM32 VCP driver - pointer becomes invalid only with optimization 【发布时间】:2018-10-23 00:34:08 【问题描述】:我正在使用 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 make
和 gcc-arm-none-eabi 5-4-2016q3
作为我的工具,ST 的 2015 年用于 STM32F405 的 vanilla 链接器和启动脚本,VCP 代码来自 2012 年 3 月。我是启动和链接器脚本的新手,但我在任何一个中都看不到任何可疑之处。我也没有在 ST 的 VCP 代码中看到任何明显的东西,但我当然不理解每一行。
我有三个问题:
-
这听起来像是堆栈溢出吗?
如何为 IRQ 分配堆栈?在 ST 的实现中,VCP 中断有一个非常深的调用树。我只需要为 VCP IRQ 分配更多吗?
-O2
中的哪些优化可能导致此行为?我想知道是否可以有选择地禁用一些可能有助于我追踪错误的优化。
【问题讨论】:
【参考方案1】:-
如果没有优化和优化有区别,这听起来与优化+易失性问题有关。好的想法是了解 volatile 类型限定符以及它与 C 优化的关系。网上有很多不错的文章。
堆栈只是存在,你不能“分配”更多的堆栈(至少在嵌入式系统上不是)。该函数可以“分配”堆栈,顺便说一下,它使用堆栈来存储局部变量、寄存器状态和移动堆栈指针。当 IRQ 发生时,当前执行状态保存在栈顶,然后执行 IRQ 处理函数。您可以通过在访问堆栈末尾的内存地址时设置断点来检测是否发生堆栈溢出。在 STM32 上,当您将某些内容放入堆栈时,堆栈指针会减小。您可以使用
-fstack-usage
检查函数的堆栈使用情况。但这与问题无关。使用更好的优化进行编译会创建使用更少堆栈的代码。
我猜全部/任何。
现在我不知道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 库都变得更好,一些最古老的库在各种优化方面存在问题。
【讨论】:
我混淆了“取消引用”这个词的含义。我的意思是指针在两条指示的行之间变得无效 - 我已经进行了编辑以澄清。 @PeterJ_01 - “隐藏的 UB”是什么意思? 所以,我猜有些代码正在覆盖pdev->regs.GREG
结构。 ep->xfer_buff += status.b.bcnt;
看起来很可疑。您可以在修改 pdev->regs.GREGS
的值时设置一个观察点,这可能会对您有所帮助。来自 STM 的驱动程序是编写的,因此一旦 pdev
在 USB_OTG_SelectCore() 中的某处初始化,regs.GREGS
不应更改其值。
另外,您可以尝试使用-O2
编译代码并将#pragma GCC optimize("O0")
或__attribute__((optimize("O0")))
添加到DCD_HandleRxStatusQueueLevel_ISR()
函数之上/到DCD_HandleRxStatusQueueLevel_ISR()
函数(也可能在USB_OTG_ReadPacket
函数之上)。这样您就可以跟踪,如果除这些功能之外的所有代码都已优化,是否出现错误。以上是关于STM32 VCP驱动——指针失效只有优化的主要内容,如果未能解决你的问题,请参考以下文章