最近搞了2块st的cortex m4 discovery 板,玩lcd玩的很开心,把我stmf1上的显示程序都移植到了新的m4平台。因为程序大部分都是用c写的,所以开始的时候非常顺利,可是在一个5参数的函数出现了问题。
症状是一运行到这个函数就进入hard fault,因为程序都是c写成的,自然不应该会有什么对齐或者试图切换arm状态的错误,于是开始调试。
当把参数减少到4个,程序就一切正常,与删减具体哪一个参数没有任何关联,参数一上5就死。这让我顿时觉得异常莫名,心底偷偷开始怀疑keil是不是有bug(当然最后的结果证明,要么是我有bug,要么是st的那群法国佬做事不地道,反正人家编译器肯定没错。。。)
于是开始跟踪反汇编代码,神奇的事情发生了,一个完全是用数组倒腾来刷屏的函数竟然出现了V开头的汇编指令!也就是说FPU的专用汇编指令竟然被使用了。keil真有bug?显然不是,仔细看代码,keil的 arm c编译器是在用FPU所携带的额外的寄存器来进行程序优化。
要知道arm这类RISC哲学的处理器,它们的高性能是有前提的,那就是不能发生“溅出”。什么叫“溅出”?所谓“溅出”就是发生超出处理器边界,访问内存的操作。一个cortex的乘法运算只需要1个周期,但是从内存中取数,写数,起码需要2+N个周期(参考权威指南)。要提高arm的性能,那么所有操作就要尽量在寄存器当中来回倒腾,而把所有对齐的内存访问组团进行以利用这个2+N的指令周期中的N。ARM处理器一个常常被MIPS拿来诟病的问题就是它只有16个寄存器,而通常的RISC机都有32个或者更多的寄存器。
cortex m4所携带的FPU本身带了32个32bit的寄存器,这在很大程度上弥补了arm通用寄存器偏少的短板,所以keil的编译器在程序优化的时候,就可以用上这32个寄存器来倒腾数据。虽然比不上真正的通用寄存器,但是总比溅出要好吧!
回到我的问题,既然我的程序的汇编中出现了FPU指令导致了hard fault那么很可能是fpu开启设置问题造成的。会不会是我的keil设置有问题?查了下,结果using fpu之类的开关完全没有任何问题。。。。那么既然st是家法国公司,它们驱动库的作者很可能是法国佬。。。下一个怀疑目标就是法国佬的做事态度了。。。。
找到固件包的CMSIS,检查system_stm32f4xx.c文件中systemInit函数:
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
很好,fpu的开启代码在这里。
于是开始抓头了,到底哪出问题了呢。。。。?
想了半天,重新检查我的程序模板中的system_stm32f4xx.c(直接用的固件库中systemtick例程改的程序模板),于是真相顿时大白了:
在SystemInit函数的相应位置,根本没有FPU setting!!!!!
这帮子法国佬,总是不大可靠的样子啊。。。。。。加上之后,顿时一切正常,再也不进hardfault了!
经过这个折腾人的问题之后,总算有了点经验,总结下:
1)不管你用不用FPU,都把FPU开着,你不用,编译器优化的时候也许会用到,提高你普通程序的性能。
2)好好检查system_stm32f4xx.c中的FPU开启部分。
3)移植操作系统的朋友,不管你的系统里用不用FPU,是不是只有单一线程用FPU,线程切换的时候FPU的32个寄存器的压栈看来是不能省的,因为你不知道编译器会不会偷偷就把它们给用了。。。。
以上是个人玩的一点浅薄经验,和大家分享哈,其实我最喜欢ti的库,写得清晰易用,还稳定。。。。5555555