总结
我正在使用最新版本的 Sourcery CodeBench Lite 工具链 (GCC arm-none-eabi 4.7.2) 将 ST 的 USB OTG 库移植到定制的 STM32F4 板上。
当我用-O0 编译代码时,程序运行良好。当我使用 -O1 或 -O2 编译时,它失败了。当我说失败时,它就停止了。没有硬故障,什么都没有(好吧,很明显它正在做一些事情,但我没有模拟器可以用来调试和查找,很抱歉。我的硬故障处理程序没有被调用)。
详情
我正在尝试调用以下函数...
void USBD_Init(USB_OTG_CORE_HANDLE *pdev,
USB_OTG_CORE_ID_TypeDef coreID,
USBD_DEVICE *pDevice,
USBD_Class_cb_TypeDef *class_cb,
USBD_Usr_cb_TypeDef *usr_cb);
...但它似乎并没有进入函数体。 (这是“stack-smashing”的症状吗?)
传递给此函数的结构具有以下定义:
typedef struct USB_OTG_handle
{
USB_OTG_CORE_CFGS cfg;
USB_OTG_CORE_REGS regs;
DCD_DEV dev;
}
USB_OTG_CORE_HANDLE , *PUSB_OTG_CORE_HANDLE;
typedef enum
{
USB_OTG_HS_CORE_ID = 0,
USB_OTG_FS_CORE_ID = 1
}USB_OTG_CORE_ID_TypeDef;
typedef struct _Device_TypeDef
{
uint8_t *(*GetDeviceDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetLangIDStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetManufacturerStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetProductStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetSerialStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetConfigurationStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetInterfaceStrDescriptor)( uint8_t speed , uint16_t *length);
} USBD_DEVICE, *pUSBD_DEVICE;
typedef struct _Device_cb
{
uint8_t (*Init) (void *pdev , uint8_t cfgidx);
uint8_t (*DeInit) (void *pdev , uint8_t cfgidx);
/* Control Endpoints*/
uint8_t (*Setup) (void *pdev , USB_SETUP_REQ *req);
uint8_t (*EP0_TxSent) (void *pdev );
uint8_t (*EP0_RxReady) (void *pdev );
/* Class Specific Endpoints*/
uint8_t (*DataIn) (void *pdev , uint8_t epnum);
uint8_t (*DataOut) (void *pdev , uint8_t epnum);
uint8_t (*SOF) (void *pdev);
uint8_t (*IsoINIncomplete) (void *pdev);
uint8_t (*IsoOUTIncomplete) (void *pdev);
uint8_t *(*GetConfigDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetUsrStrDescriptor)( uint8_t speed ,uint8_t index, uint16_t *length);
} USBD_Class_cb_TypeDef;
typedef struct _USBD_USR_PROP
{
void (*Init)(void);
void (*DeviceReset)(uint8_t speed);
void (*DeviceConfigured)(void);
void (*DeviceSuspended)(void);
void (*DeviceResumed)(void);
void (*DeviceConnected)(void);
void (*DeviceDisconnected)(void);
}
USBD_Usr_cb_TypeDef;
我已尝试包含与此问题相关的所有源代码。如果您想查看完整的源代码,可以在这里下载:http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stm32_f105-07_f2_f4_usb-host-device_lib.zip
尝试的解决方案
我尝试使用 #pragma GCC optimize ("O0")
、__attribute__((optimize("O0")))
,并将某些定义声明为 volatile
,但没有任何效果。我宁愿只修改代码以使其与优化器配合得很好。
问题
我如何修改此代码以使其与 GCC 的优化器兼容?
最佳答案
您显示的代码似乎没有任何问题,所以这个答案会更笼统。
“接近硬件”的代码在未优化的情况下正常工作并且在更高的优化级别下失败的典型错误是什么?
想想 -O0
和 -O1/-O2
之间的区别:优化策略包括 - 循环展开(似乎并不危险),尝试尽可能长时间地在寄存器中保存值、消除死代码和重新排序指令。
如果可以随时更改的硬件寄存器未正确声明volatile
,则改进的寄存器使用通常会导致更高优化级别的问题(请参阅上面 PokyBrain 的评论)。优化后的代码将尝试尽可能长时间地在寄存器中保存值,从而导致您的程序无法注意到硬件方面的变化。 确保正确声明硬件寄存器 volatile
如果您需要读取硬件寄存器以对编译器不知道的硬件产生任何影响并且不对您刚刚读取的值执行任何操作,则消除死代码可能会导致问题。如果您没有正确声明用于读取访问的变量 void
,这些硬件访问可能会被优化掉(尽管编译器应该发出警告)。 确保将虚拟读取转换为 (void)
指令重新排序:如果您需要按特定顺序访问不同的硬件寄存器以产生所需的结果,并且如果您通过与其他方式无关的指针来执行此操作,则编译器可以自由地重新排序结果指令,因为它认为合适(即使 if 硬件寄存器 are 正确声明为 volatile
)。您需要在代码中加入内存屏障以强制执行所需的访问序列 (__asm__ __volatile__(::::"memory");
)。 确保在需要的地方添加内存屏障。
虽然不太可能,但您仍然可能发现编译器错误。优化不是一件容易的事,尤其是当涉及到硬件时。可能值得查看一下 gcc 错误数据库。
如果所有这些都没有帮助,有时您就无法避免深入研究生成的汇编代码以确保它按照预期的方式工作。
关于c - GCC 4.7.2 优化问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15399236/