gasbi 发表于 2010-8-17 13:47:57

KEIL MDK优化问题

用KEIL MDK uVision4,如下程序,GPS_flg 是一个全局变量,当有GPS数据时就会中断被置1,结果我在while(1)外面判断GPS_flg就始终为0,进入死循环出不来,在while(1)里面就可以判断到GPS_flg为1,最后找不到问题,把优化level1改成level0就好了。折腾了半天时间,难道优化这么恐怖?

main()
{...       
while(GPS_flg == 0)
{;}
while(1)
{
    if(GPS_flg == 0)
      i++;
    if(GPS_flg == 1)
    {       
      xprintf("i = %d\r\n", i++);
      i=0;
    }
...
}
}

june4th 发表于 2010-8-17 14:39:58

volatile

gasbi 发表于 2010-8-17 16:24:05

回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------

试过了思密达

dr2001 发表于 2010-8-17 17:06:41

看汇编斯密达。

Keil不优化的时候,所有变量用到时必然会从内存读。开了优化就可能寄存器。
考虑:更新编译器版本。

june4th 发表于 2010-8-17 17:51:43

回复【2楼】gasbi Glen
回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------
试过了思密达
-----------------------------------------------------------------------

IAR如何?

823032003 发表于 2010-8-17 21:57:46

中断要设置的变量 最好 volatile

ADO1234 发表于 2010-8-17 22:16:41

中断要设置的变量 最好 volatile

这个是对的!呵呵!

huchunlei 发表于 2010-9-25 02:46:54

楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个变量是可能随时发生变化的,使得编译器编译程序的时候,每次都从RAM里面读取数据,而不是使用之前缓存到寄存器里面的值。

同样,对于多任务的程序,如果一个公共变量被多个任务用到也要加volatile修饰。

比如    volatile unsigned char GPS_flg;

程序写的严谨,什么编译器都没问题的。

ohmytime 发表于 2011-3-11 23:56:28

大大的明白,思密达!

qiushui 发表于 2011-3-16 20:53:39

关键字volatile有什么含意?并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。
嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

qiushui 发表于 2011-3-16 20:54:34

1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
   return *ptr * *ptr;
}

zclcom79 发表于 2011-3-20 20:30:57

volatile 正解

w492901074 发表于 2012-7-31 21:40:44

刚接触keil mdk 变量问题貌似是最大问题 {:dizzy:} 解决变量问题,mdk就算精通了、吧

cy_ygs 发表于 2012-8-1 10:40:23

学习。。。

orange-208 发表于 2012-8-4 00:06:49

懂一点点的路过!

wade230506 发表于 2012-8-29 14:10:52

学习到了,
volatile变量的用法

myhonour 发表于 2012-9-18 23:12:30


不错,谢谢

tanwolf 发表于 2012-9-23 21:07:24

学习了,以前没碰到这问题

zhzhchang 发表于 2012-9-26 15:19:53

本帖最后由 zhzhchang 于 2012-9-26 15:33 编辑

好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不规范的代码,才得以发现这个奇葩的事件。实在忍不住用了两个奇葩来形容。把过程简化一下,如下所述:

假如你的工程至少有两个.c文件,其中一个为timer.c,里面有个定时器中断程序,每10ms中断一次,定义一个变量来统计定时器中断次数:


unsigned int unIdleCount;还有一个timer.h文件,里面是一些timer.c模块的封装,其中变量unIdleCount就被封装在里面:


extern unsigned int unIdleCount;


在main.c函数中,包含timer.h文件,并利用定时器变量unIdleCount来精确延时2秒,代码如下:


unIdleCount=0;
   
while(unIdleCount!=200);   //延时2S钟
keil MDK V5.54下编译,默认优化级别,编译后下载到硬件平台。你会发现,代码在while(unIdleCount!=200);处陷入了死循环。反汇编,代码如下:


   122:   unIdleCount=0;
   123:      
0x00002E10E59F11D4LDR       R1,
0x00002E14E3A05000MOV       R5,#key1(0x00000000)
0x00002E18E1A00005MOV       R0,R5
0x00002E1CE5815000STR       R5,
   124:   while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E20E35000C8CMP       R0,#0x000000C8
0x00002E241AFFFFFDBNE       0x00002E20
重点看最后两句汇编代码,寄存器R0是当前变量unIdleCount的值,汇编指令CMP为比较指令,如果R0中的内容与0xC8不等,则循环。但是这里并没有更新寄存器R0的代码,也就是说变量unIdleCount的值虽然在变化,但跟0xC8一直比较的却是内容不变的R0。因为之前变量unIdleCount被清零,所以R0的内容也是0,永远不等于0xC8,永远不会跳出循环。

看到这里,也许你已经笑翻了:你这个小白,这很明显是没用volatile修饰变量unIdleCount造成的!!!不错,比起从RAM中读写数据,ARM或其它硬件从寄存器读取数据要快的多的多的多...因此编译器会“自作主张”的将某些变量读到寄存器中,再次运算时也优先从寄存器中读取,上面的例子就是这样。解决这样的方法是用关键字volatile修饰你不想让编译器优化的变量,明白的告诉编译器:你不准优化我,每次使用我你都要本本分分的从RAM中读取或写入RAM。

所以先不要笑,我是不会犯这种错误的,之所以从这里说起,是为了照顾下还不知道volatile关键字的。。。

其实在timer.c中我是这样定义统计定时器中断次数变量的:


unsigned int volatile unIdleCount;但是,在timer.h中,我确偷了个懒,声明这个变量的代码如下:

extern unsigned int unIdleCount;
没有使用关键字volatile,在keil MDK V5.54下编译,默认优化级别,然后查看代码的反汇编,如下所示:


   122:   unIdleCount=0;
   123:      
0x00002E10E59F11D4LDR       R1,
0x00002E14E3A05000MOV       R5,#key1(0x00000000)
0x00002E18E1A00005MOV       R0,R5
0x00002E1CE5815000STR       R5,
   124:   while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E20E35000C8CMP       R0,#0x000000C8
0x00002E241AFFFFFDBNE       0x00002E20

可以看出,这个反汇编代码居然和没加volatile关键字的时候一模一样!!代码还是会在while出陷入死循环。

现在,应该知道我要表达的意思了吧,如果引用的变量声明中没有使用volatile关键字修饰,即便定义这个变量的时候使用了volatile关键字修饰,MDK编译器照样优化掉它!

将timer.h中的声明更改为:


extern unsigned int volatile unIdleCount;

同样环境下编译,查看反汇编代码,如下所示:


   122:   unIdleCount=0;
   123:      
0x00002E10E59F01D4LDR       R0,
0x00002E14E3A05000MOV       R5,#key1(0x00000000)
0x00002E18E5805000STR       R5,
   124:   while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E1CE5901000LDR       R1,
0x00002E20E35100C8CMP       R1,#0x000000C8
0x00002E241AFFFFFCBNE       0x00002E1C
看最后三句汇编代码,发现多了一个载入汇编指令LDR,这个指令在每次循环中都将变量unIdleCount从RAM中读出到寄存器R1中,然后R1的值再和0xC8比较。这才是符合逻辑的需要的代码。

以这个为例子,一是说明关键字volatile,另外也提下这个有趣的编译器,不得不说,她好认真。再另外,我要买本编译原理的书看看了.

摘自个人博客---有趣的MDK细节:http://blog.csdn.net/zhzht19861011/article/details/7745151

现则反过来想想,原因还是很简单的,MDK编译多个文件时是分别编译,最后再用链接器链接,当编译的时候一个模块引用另外一个模块的变量,完全是靠的变量声明,如果声明都不加volatile,那么引用的模块肯定会把变量当成普通变量的,再反推一下,如果原变量没有加volatile,但是声明的时候加了volatile,是不是引用的模块会将这个变量当成volatile型变量呢.

armstrong 发表于 2012-10-7 13:14:17

zhzhchang 发表于 2012-9-26 15:19 static/image/common/back.gif
好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不 ...

你这是对C编译原理的不了解导致的。
如果我告诉你一个最简单的基础知识,你就会对这些现象融汇贯通了:C编译器是以每个C文件作为基本编译单元的,称为模块,被编译为obj;而模块之间的函数或变量访问都是通过标号来实现的,标号本身没有任何属性,只是提供给链接器使用的一个符号名称而已,标号的属性完全就靠调用的地方的原型声明来决定的!
因此,你在timer模块中定义为volatile,仅仅是在timer模块中告诉编译器不要优化而已,在另外的模块内使用了这个变量,而它们是不知道该变量是什么属性的,所以只有靠原型声明来告诉编译器这些信息了。
最后简单的给你总结一下:别小看原型声明,它是联系各个模块的桥梁,是各种输出符号的属性信息所在。

cloudborn123 发表于 2012-10-11 11:07:00

很好!表示学习!

cumtgao 发表于 2012-10-11 12:29:59

是不是因为中断始终慢于main函数中非死循环部分的语句?

广陵散曲 发表于 2012-12-24 16:41:23

mark{:smile:}
好经验,收藏了

chu 发表于 2013-1-1 20:00:18

学习了!

BeichenPeng 发表于 2013-1-2 10:07:45

GPS_flg 这个变量在while以前应该被用到了,所以会存在寄存器中;如果while是main的第一条语句,不会出现这个问题吧。

guowanling8061 发表于 2013-1-8 15:43:15

huchunlei 发表于 2010-9-25 02:46 static/image/common/back.gif
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...

写的太具体了!!学习了!!

Julius20110 发表于 2013-1-10 23:20:30

非常的好   还是的看看编译原理的书籍了

xiefy21 发表于 2013-8-12 20:11:19

mark……
顶一个…

longbiao1218 发表于 2013-9-10 21:28:05

mark很好学习了

zwj512 发表于 2013-9-12 14:15:30

huchunlei 发表于 2010-9-25 02:46 static/image/common/back.gif
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...

受教啦                                             

洋芋擦擦 发表于 2013-11-28 19:11:53

今天 刚碰到这个问题   
mark 一下

木君之上 发表于 2014-2-24 01:16:00

zhzhchang 发表于 2012-9-26 15:19
好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不 ...

1、你好,我发现如果那个while(unIdleCount!=200);   不是一个空等待的循环,(当然,里面不是的环就不是等待2Ms了,而是在这2MS里一直执行某个操作了),这样的话就不会出现这种情况了,在比较之前
   会有一个LDR的操作,保证读取的是RAM里的数据,而这时我并没有用volatile修饰他,而且我的优化等级选的是最高的3也没关系

2、另一个,我发现,只要unIdleCount=0不是紧挨着下面那个判断语句,两句之间隔了一些函数,就算这些函数没有操作unIdleCount,在比较之前也会有那个LDR命令获得unIdleCount最新的值

   22:         TIM3_Int_Init(500,7199);//10Khz的计数频率,计数到5000为500ms   
    23:         
0x08000A08 8820      LDRH   r0,   //这条语句是在下面那个判断之前突然出现的,用来获得最新的值
    24:         while(unIdleCount!=10);
    25:                  
0x08000A0A 280A      CMP      r0,#0x0A
0x08000A0C D1FD      BNE      0x08000A0A

同样,这时我也没有用volatile修饰unIdleCount,那应该就是这时寄存器里面没有保存unIdleCount了,(因为这之前的操作保存不了了),所以会重新读,

3、后来我又NC的做了几个实验,发现在unIdleCount=0;和while(unIdleCount!=10);之间相隔的有东西的时候,不管是函数(需要跳转)还是简单运算,只要编译器有办法用一个寄存器保存unIdleCount 的值
   同时又能完成这之间的运算,他就会直接比较寄存器里面的值,(此时我任然选的是最高优化级别)我的是4.7版本的MDK。
   于是我又重复了1里面说的,发现只要while里面有东西,哪怕简单得足以让可以保存unIdleCount的值,但是他也会每次都读取最新的值,
   于是我真的有点感慨,真的很智能,(他的理念或许就是这样),完全没意义的事就不用每次都浪费时间和代码空间读取最新的值了,但是
    真的有事要做的话就得严谨地每次读取最新的值看是不是满足条件,应不应该执行了,哈哈,说的优点多


xjtyOnly51 发表于 2014-4-8 16:38:37

volatile 的使用{:smile:}

skylinyk 发表于 2014-4-10 16:46:45

路过学习了,很好!

llb126yx 发表于 2014-5-16 21:40:06

学习了,不过20楼的意思没怎么明白,还望再解释下

sinxcosytana 发表于 2014-5-22 21:50:56

mark!学习备用,虽然还是有点没明白

天行者 发表于 2014-6-24 15:13:33

不错。MARK一下。

别有洞天下 发表于 2014-9-18 08:50:54

优化当然恐怖,一个好的程序难就男在优化上了

youthdou 发表于 2014-9-18 09:04:40

qiushui 发表于 2011-3-16 20:54
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么 ...

看来我就是个C程序员。记住了volatile。

hyf88 发表于 2014-11-19 14:37:51

学习了,以前没碰到这问题

chengsong 发表于 2014-11-19 15:08:34

做个标记,有时候程序不知道原因跑错。

zhongjp85 发表于 2014-11-19 16:45:45

mark一下学习~~~~~

freefei 发表于 2014-11-24 14:18:47

volatile的用法这么有讲究,回头好好研究一下

dj1981812 发表于 2014-11-24 15:27:45

volatile的用法很有讲究,大家好好理解呀。

cyt 发表于 2014-11-25 11:13:06

对于没有用于左值运算的变量,程序会进行优化,此时就应该加VOLATILE了。空循环也是容易被优化的。请问楼主的问题找到了吗?我的程序也是没有优化的时候就正常,优化后有些功能不异常。

hongguan 发表于 2014-11-27 14:12:43

volatile的用法,谢谢了。

彼岸花开@ 发表于 2014-11-28 07:29:25

优化,下午试一下。。从来没有优化过代码

爺@龙行天下 发表于 2014-11-28 20:06:54

受教了

kevin_me 发表于 2014-11-28 20:43:16

养分很足,电脑上再去好好看

levinxia 发表于 2015-1-4 11:22:32

有时候全局变量在其它中断程序里面有修改的话。也会出现一些奇怪的问题。

沉默胜过白金 发表于 2015-2-8 18:32:15

好帖子,mark ,volatile ~~

一杯茶2009 发表于 2016-4-22 16:51:31

学习了,感谢分享

ywlzh 发表于 2016-5-9 13:34:40

有时候还是搞不懂,程序跑的好好的,为什么还要优化

myhonour 发表于 2016-5-10 13:45:41

volatile 正解

hefeizdl 发表于 2016-11-11 18:27:16


mark
好经验,收藏了
页: [1]
查看完整版本: KEIL MDK优化问题