打印

奇怪的常数数组错误(H-ITECH V9.82)

[复制链接]
4439|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 与时俱进 于 2012-10-17 17:39 编辑

定义好包含100个整形常数的数组S[100],程序访问该数组时出现这种怪现象:前64个正常,后36个即从S[64]到S[99]都变成0xffff,这是为什么?
沙发
与时俱进|  楼主 | 2012-10-10 10:53 | 只看该作者
HI-TECH 版本V9.82,跟踪到程序存储器,常数数组S[100]存放在0x401起始的连续200字中,访问前64个正常,后36个却访问错误的地址,比如S[63]访问地址0x47f,S[64]却访问到0x381,而S[64]的正确地址应该是0x481!
这个应该是编译器的BUG吧?

使用特权

评论回复
板凳
Ryanhsiung| | 2012-10-10 13:43 | 只看该作者
溢出了吗,看一下RAM的使用情况

使用特权

评论回复
地板
yewuyi| | 2012-10-10 17:30 | 只看该作者
PIC的RAM是分页的,编译器会自动根据变量分配情况调整较大数组型变量的地址分配,如果使用中读到的数组元素值是对的,就无需关心这个,一切交给编译器处理好了。

使用特权

评论回复
5
与时俱进|  楼主 | 2012-10-11 17:59 | 只看该作者
大家没明白我说的。
把下面的程序做个调试就明白啦。
#include <pic.h>   
unsigned int i,j;
const unsigned int s[100]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99};

void  main()              
{       
        while(1)
        {        i=s[63];   //这条执行完,i=0x3f是对的
                j=s[64];  //这条执行完,j=0,错了!
                LATC=i;
                LATB=j;         
        }
}

使用特权

评论回复
6
与时俱进|  楼主 | 2012-10-11 18:23 | 只看该作者
本帖最后由 与时俱进 于 2012-10-12 08:50 编辑

编译后的代码(Disassembly Listing):
9:                         while(1)
10:                        {        i=s[63];
  07EA    3084     MOVLW 0x84    //地址低位
  07EB    0084     MOVWF 0x4     //FSR0L
  07EC    3080     MOVLW 0x80   //bit7=1,读程序区;地址高位
  07ED    0085     MOVWF 0x5    //FSR0H
  07EE    3F00     MOVIW [0]FSR0
  07EF    00F0     MOVWF 0x70
  07F0    3F01     MOVIW [1]FSR0
  07F1    00F1     MOVWF 0x71
11:                                j=s[64];
  07F2    3086     MOVLW 0x86   //地址低位
  07F3    0084     MOVWF 0x4     //FSR0L
  07F4    307F     MOVLW 0x7f    //bit7=0,读数据区;地址高位
  07F5    0085     MOVWF 0x5     //FSR0H
  07F6    3F00     MOVIW [0]FSR0
  07F7    00F2     MOVWF 0x72
  07F8    3F01     MOVIW [1]FSR0
  07F9    00F3     MOVWF 0x73
---------------------
其中0x4和0x5分别是FSR0L和FSR0H;从上面可以看出,执行i=s[63]时把FSR0H(0x80)的bit 7置1,访问的是程序区(地址0x084),而执行j=s[64]时把FSR0H(0x7f)的bit 7清0,访问的是数据区(地址0x7f86),这显然是不对的!
编译器是HI-TECH V9.82,上面明显的错误应该是它的bug吧?

使用特权

评论回复
7
与时俱进|  楼主 | 2012-10-11 18:29 | 只看该作者
注意上面的整形数组s[100]是常数,const修饰。
从下面program memory可以看出,s[100]数组存放在程序的连续空间里,这是对的。

Line  Address  Opcode               Disassembly              

   125   007C    343B  RETLW 0x3b                             
   126   007D    3400  RETLW 0                                
   127   007E    343C  RETLW 0x3c                             
   128   007F    3400  RETLW 0                                
   129   0080    343D  RETLW 0x3d                             
   130   0081    3400  RETLW 0                                
   131   0082    343E  RETLW 0x3e                             
   132   0083    3400  RETLW 0                                
   133   0084    343F  RETLW 0x3f         //这个是s[63]                    
   134   0085    3400  RETLW 0                                
   135   0086    3440  RETLW 0x40         //这个是s[64]                     
   136   0087    3400  RETLW 0                                
   137   0088    3441  RETLW 0x41                             
   138   0089    3400  RETLW 0                                
   139   008A    3442  RETLW 0x42                             
   140   008B    3400  RETLW 0                                
   141   008C    3443  RETLW 0x43                             
   142   008D    3400  RETLW 0                                
   143   008E    3444  RETLW 0x44                             
   144   008F    3400  RETLW 0                                
   145   0090    3445  RETLW 0x45                             
   146   0091    3400  RETLW 0                                
   147   0092    3446  RETLW 0x46                             
   148   0093    3400  RETLW 0                                
   149   0094    3447  RETLW 0x47                             
   150   0095    3400  RETLW 0                                
   151   0096    3448  RETLW 0x48                             
   152   0097    3400  RETLW 0                                
   153   0098    3449  RETLW 0x49                             
   154   0099    3400  RETLW 0                                
   155   009A    344A  RETLW 0x4a                             
   156   009B    3400  RETLW 0                                
   157   009C    344B  RETLW 0x4b                             
   158   009D    3400  RETLW 0

使用特权

评论回复
8
yewuyi| | 2012-10-13 12:37 | 只看该作者
重要的是LATB=j;  这句执行后结果对不对?


PICC可能识别出在当前的这几行代码中,j=s[64];  属于无意义的代码,自动给你优化掉了,可能直接变成了LATB=j; 这行代码。

另外LATB=j; 本身带有隐含的强制类型转换,按照我对工程师的要求,所有的强制类型转换都必须使用显式,不依靠编译器的任何默认强制转换规则。

使用特权

评论回复
9
与时俱进|  楼主 | 2012-10-13 21:09 | 只看该作者
本帖最后由 与时俱进 于 2012-10-13 22:08 编辑

8# yewuyi
谢谢!上面只是测试程序,为了 i=s[63]; j=s[64];俩条语句不被优化掉,特意在它们后面加上 LATC=i;  LATB=j;
从6楼可以看出来  j=s[64]; 没有被优化掉。
哪位用HI-TECH V9.82的大虾,请帮忙试试上面的程序吧。

使用特权

评论回复
10
virtualtryon| | 2012-10-13 22:30 | 只看该作者
我记得以前也碰到过类似的问题,应该是编译器的BUG,可能是没有办法找到200个Byte连续的程序空间,把const unsigned int改为const unsigned char试试。

使用特权

评论回复
11
与时俱进|  楼主 | 2012-10-15 08:43 | 只看该作者
本帖最后由 与时俱进 于 2012-10-15 08:53 编辑

8# yewuyi 报告:LATB=j;执行结果LATB为0是错的。强制类型转换后结果也一样。

使用特权

评论回复
12
与时俱进|  楼主 | 2012-10-15 08:48 | 只看该作者
我记得以前也碰到过类似的问题,应该是编译器的BUG,可能是没有办法找到200个Byte连续的程序空间,把const unsigned int改为const unsigned char试试。
virtualtryon 发表于 2012-10-13 22:30
把数组改成char型,运行正常。但是这只能说明char型数组正常,不能说明问题。
从7楼可以看出S[100]存放在连续的200字程序空间里。

使用特权

评论回复
13
yewuyi| | 2012-10-15 10:12 | 只看该作者
const 已经指定放到ROM中了,如果ROM不够,会连接不成功,所以不存在10楼的问题。我当前最高只用到了PICC9.80版本,在我的某些代码中,有一个const unsigned int R[161]的常量数组,我从PICC8.05-->9.05-->9.50-->9.80这几个版本的使用过程来看,从未遇到过这种常量数组的错误。 为了测试你的代码,我在我某个项目文件中插入了你的程序片段,在MPLAB8.86 SIM+PICC9.80环境下验证,没有任何问题,截图如下。

使用特权

评论回复
14
与时俱进|  楼主 | 2012-10-15 13:09 | 只看该作者
谢谢楼上!非常感谢!
我原来用的版本9.50也这么用过,没出现这个问题。

使用特权

评论回复
15
与时俱进|  楼主 | 2012-10-15 13:21 | 只看该作者

又实验了一番,有新发现。

原来是否出错还跟芯片选择有关!用芯片F914就运行正常(后面的LATC等做相应修改),F1937就不行。

使用特权

评论回复
16
virtualtryon| | 2012-10-15 20:50 | 只看该作者
PICC9.8+PIC16F1936是会出问题的。我猜测应该是不同的单片机,不同的编译器,ROM空间大小,和程序的放置是不一样,至少可以肯定的是,PICC应该是有BUG的,至于为什么有这样的BUG,只能猜测。或者是连续空间分配的时候出问题了。
所以我们在编程的时候有一项代码审查项是,如果有复杂的指针操作,比如指向指针的指针,或者是大空间的地址分配,都是查看汇编代码。
而且每一个单元模块都要有严格的单元测试。
做产品应该对所有的因素都证真,而不是证伪。
单片机和编译器非常容易出问题的。

使用特权

评论回复
17
yewuyi| | 2012-10-17 09:02 | 只看该作者
针对楼上反映的情况,我再次验证了一下:
void main() {
i=s[63]; //这条执行完,i=0x3f是对的
j=s[64]; //这条执行完,j=0,错了!
LATC=i;
LATB=j;
}
如果是以上的代码,则编译后,楼主反映的情况确实存在。
但当我在其后加入其他代码后,如:
void main() {
i=s[63]; //这条执行完,i=0x3f是对的
j=s[64]; //这条执行完,j=0,错了!
LATC=i;
LATB=j;  
Initsys();//随意拷贝的一段代码
Clrwdt();//随意拷贝的一段代码
Keydecode();//随意拷贝的一段代码
DispScan();//随意拷贝的一段代码
}
编译这个之后,楼主反映的情况消失。
---------------------------------------------------------
综合以上,不排除PICC在这个问题上存在BUG的可能,但估计这个BUG还是因为优化问题造成的,也许编译器把第一段代码识别为无意义代码而自动优化掉了,应当还是编译器优化不当出现的问题。



DispFlash();

使用特权

评论回复
18
与时俱进|  楼主 | 2012-10-17 10:23 | 只看该作者
百忙抽空,非常感谢!
楼上芯片是选择什么型号?选择16F1936试试。为了验证我以下的观点,我按照楼上的后一种方法验证一遍,还是错。
    从6楼编译后的代码分析,并没有对j=s[64]进行优化.
    只是把i=S[63]     编译成   (FSR0=0X8084)------> i , 从code地址0x84取数给i
    把j=S[64]     编译成    (FSR0=0X7f86)------> j ,从ram地址0x7F86取数给j
    注意S[64]的地址应该编译成0x8086而不是0x7f86,这就是编译错误之处。

使用特权

评论回复
19
yewuyi| | 2012-10-17 10:30 | 只看该作者
1933,1936,1938我都试过了,都是正确的。

使用特权

评论回复
20
yewuyi| | 2012-10-17 10:37 | 只看该作者
经过检查,只要你后面没有有效操作IO端口B的指令,编译都会自动把j=s[64]; 优化处理掉。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

75

主题

1290

帖子

2

粉丝