在试验SDIO 代码的时候发现了一个明显的运算错误:
测试环境:
2G SD 卡, STM32
IAR Assembler for ARM 4.42A (4.42.1.501)
编译优化:none
代码:
SDIO 的 SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo) 函数;
... ... ...
/* Byte 9 */
tmp = (u8)((CSD_Tab[2] & 0x00FF0000) >> 16);
cardinfo->SD_csd.MaxWrCurrentVDDMin = (tmp & 0xE0) >> 5;
cardinfo->SD_csd.MaxWrCurrentVDDMax = (tmp & 0x1C) >> 2;
cardinfo->SD_csd.DeviceSizeMul = (tmp & 0x03) << 1; // 执行后 cardinfo->SD_csd.DeviceSizeMul=0x06
/* Byte 10 */
tmp = (u8)((CSD_Tab[2] & 0x0000FF00) >> 8); // 执行后 tmp=0xFF
cardinfo->SD_csd.DeviceSizeMul |= (tmp & 0x80) >> 7; // 执行后 cardinfo->SD_csd.DeviceSizeMul=0x6F
// 正确的代码应改为 0x07
... ... ...
跟踪了一下汇编发现,关键的语句是
ORRS R3,R3,R2,LSR #7 // 其中 R3=0x06 对应于 cardinfo->SD_csd.DeviceSizeMul
// R2=0x002DB7FF 对应于 tmp
错误在于没有对 R2 做取低8位处理,导致 R2 右移将高位数据移到了低8位。
简化程序做了一下试验,下面的三种语句都是一个结果 BB=0x68,显然是不对的。
编译优化:none
u8 AA,BB=0;
u32 CC;
CC = 0x12345678;
AA = CC >> 8;
BB |= (AA & 0x80) >> 7; // BB=0x68
AA = (u8)CC >> 8;
BB |= (AA & 0x80) >> 7; // BB=0x68
AA = (u8)(CC >> 8);
BB |= (AA & 0x80) >> 7; // BB=0x68
显然很危险,问题是如何有效的避免这种Bug 呢?
有IAR高版本的同行可以试验一下,是否改掉了这个错误。
不可能吧!
ORRS R3,R3,R2,LSR #7 前面应该还有条UXTB指令
EWARM5.20上没有问题!
测试的屏幕拷贝
u8 AA,BB;
u32 CC;
CC = 0x12345678;
BB =0;
AA = CC >> 8;
BB |= (AA & 0x80) >> 7;
// AA=0x56 BB=0x68 错
BB =0;
AA = (u8)(CC >> 8);
BB |= (AA & 0x80) >> 7;
// AA=0x56 BB=0x68 错
BB |= ((AA & 0x80) >> 7);
回答 mcuisp, 软件和硬件单步的结果都是错的
回答 mcuisp, 软件和硬件单步的结果都是错的。
正确的结果 BB的值应该为0x00.
mcuisp 你试验的环境和我一样吗? 包括版本和优化级别
另外, 编译CPU 是 ST STM32F10xxE
楼上老狼提出的使用下面的语句
BB |= ((AA & 0x80) >> 7);
其结果 BB=0x68 仍然是错的
请注意:局部变量被分配到寄存器,不能在Watch窗口查看中间
Watch窗口始终是显示变量对应的内存中数据的变化,如果在变量参与运算时是在寄存器中进行,则Watch窗口不能反映实际变量数值的变化。
请使用全局变量定义AA和BB,然后再操作结束后再观察Watch窗口。
针对版主的建议的试验结果,仍然不对
我设计了一个全局变量 DD,执行完后仍然不对,代码如下:
static u8 DD;
int main(void) {
u8 AA,BB;
u32 CC;
CC = 0x12345678;
BB =0;
AA = CC >> 8;
BB |= (AA & 0x80) >> 7;
DD = BB;
// AA=0x56 BB=0x68 DD=0x68 错
其实,如果看看汇编的情况,可以估计是编译器的问题
请问,在单步操作过程中,是否看到watch窗口的内容有变化?
不好用?
做以下实验
将CPU 改为STM32F10x ,然后用软仿真,试一下,我的那个语句应该是正确的,如果是这样,说明你可能没有打补丁,去ST下一个吧,或者去万历的网站,因为4.42版本出来的早!(那个升级包有时候不太好用,建议直接下一个5.2的版本,重新装)
如果还是有问题,建议将软件改为
u8 AA=0;
u8 BB=0;
u8 dd=0;
u32 CC;
BB=0;
CC = 0x12345678;
AA = CC >> 8;
//BB |= (AA & 0x80) >> 7;
dd = (AA & 0x80); // BB=0x68
dd=dd>>7;
BB = BB|dd; // BB=0x68
一步步跟踪!
Re
应该和C的整型位操作扩展有关系。没认真学习C标准,不知道标准对这个是怎么规定的。
但是拿8Bit进行计算的时候,有可能编译器会默认扩展到32位去算,位操作,要么强制类型转换,要么就需要去自己与上个东西缩小到合适的范围。
MISRA建议貌似是强制与范围。
我没有打任何补丁.4.42版,优化None
呵呵,我也想看IAR出错呢。
此问题的阶段小结
1. 下载的V5.3 (32K限制版)同样程序试验,结果OK。 正如2楼所说V5.3编译器在该指令之前加了一个UXTB指令
UXTB
ORRS R3,R3,R2,LSR #7
2. 同样一段程序在不同版本的编译器下的执行结果不一样,显然是编译器的Bug导致。而不是我们程序的表达问题。
3. 楼上老狼提出的针对4.42A的编译器的补丁,我没有找到。只是看到一些MCU器件扩充的补丁,相信这些补丁不会影响编译Bug。如果有同行发现4.42A的编译器的补丁下载,请通报一下。
4. 楼上的 mcuisp 始终没有验证这个问题,建议你看看汇编代码,是否含有UXTB指令。或者贴上你的调试画面。
Thanks
同样的程序拷到万利的例子里,出现了楼主所说的Bug
呵呵。
我先前的试验里,AA/BB/CC都被分配到了SRAM里。
而万利的例子AA/BB/CC分别是R4,R5,R6
应该算是IAR的Bug了。
支持楼主!!!
IAR4.42是有这个问题
我曾经找IAR的工程师确认过了,估计是前期版本的bug,在IAR5.2就好了。
所以说,请大家用最新的编译器咯。
实验了一下,真有这个问题
太可怕了,不知道还有多少编译器隐含的问题。
为避免这类问题不知道在编程上需要注意些什么?