资源预览内容
第1页 / 共4页
第2页 / 共4页
第3页 / 共4页
第4页 / 共4页
亲,该文档总共4页全部预览完了,如果喜欢就下载吧!
资源描述
导致导致 STM32STM32STM32STM32 芯片指令速度变化的问题分析过程芯片指令速度变化的问题分析过程过年那几天将一份代码从 TI 的 LM3S8962 芯片移植到 ST 的 STM32F103VB 芯片上, 结果发现了 STM32 芯片指令速度会发生变化,本文将讲述这个问题的定位过程,从中你可 以看到作者根据问题的现象结合已有的知识,2 次否定了出问题的地方,但随着逐步缩小定 位范围,认真分析现象,最终还是找回到了出问题的地方,并与网友讨论后,查找芯片手册 找到了问题的原因。 本文的重点不在于介绍这个问题, 而是在于介绍定位这个问题的思路以 及过程,很多问题通过仔细分析是可以找到原因的。现象描述现象描述下面这段延迟时间的函数在 LM3S8962 芯片上可以产生正确的延迟时间,但在 STM32F103VB 芯片上却表现出不确定性,有时候可以延迟正确的时间,而有时候则变为正 确时间的 1.25 倍。比如,输入 200,正常的延迟时间是 2000ms,而出现异常时这段代码则 运行了 2500ms。void DEV_DelayMs(U32 uiMs) unsigned int i; unsigned int j;j = 5998 * uiMs;for(i = 0; ; i+) if(i = j) break; 背景知识介绍背景知识介绍我写了 2 个具有任务切换功能的小型操作系统, 其中一个我称之为 wanlix, 需要函数主 动调用任务切换函数才能发生任务切换,只有这一个功能,功能虽少,但也非常小巧,编译 后只有几百字节,适合程序空间只有几十 K 的小系统使用。另一个我称之为 mindows,是 实时抢占式的内核, 高优先级的任务会自动抢占低优先级的任务, 支持信号量、 队列等功能。 更多信息,请访问我的新浪博客 blog.sina.com.cn/ifreecoding 其中 DEV_DelayMs 函数用来在任务中产生延迟,模拟任务的业务,其中 mindows 拥有 tick 定时器,操作系统打印出的 tick 时间会反应出 DEV_DelayMs 函数的执行时间。 LM3S8962 芯片与 STM32F103VB 芯片指令速度不一样, 因此在移植这 2 个操作系统时 对 DEV_DelayMs 函数内的 i、j 数值做了精确的调整,并使用了 O0(哦零,不优化)优化 选项,使之能产生精确的延迟。DEV_DelayMs 函数在 LM3S8962 芯片上工作正常,但在 STM32F103VB 芯片上却有时正常,有时异常。原因分析原因分析mindows操作系统的打印带有 tick 时间 (每个 tick 是 10ms) , 这个问题最先是在 mindows 上发现的。程序中有一段 2000ms 的延迟,正常时,串口打印出这段延迟时间是 2000ms,如下所示:TaskTest1 - Task Test2! Tick is: 200 Task2 is running! Tickis:200但异常时打印却变成了 2500ms,如下所示:TaskTest1 - Task Test2! Tick is: 250 Task2 is running! Tickis:250出现这个问题,有可能是 tick 时间不准,也有可能是 DEV_DelayMs 函数时间不准。 在 运行时对比钟表的时间,发现 tick 时间是准的,那么说明是 DEV_DelayMs 函数时间不准。 既然是 DEV_DelayMs 函数不准,那么很可能是 DEV_DelayMs 函数在编译时生成的汇 编指令不一样,导致 DEV_DelayMs 函数执行时间的长短不一样,而且,这个异常还伴随着 一个特点异常与编译是相关的,也就是说编译后一旦出现异常,那么无论复位多少次, 异常一直出现,编译后一旦正常,那么无论复位多少次,均不会出现异常。这一点使我更加 坚信了是编译器编译出了不同的代码导致了问题。为了证明这一点,将正常和异常时 的 DEV_DelayMs 函数进行反汇编,对比汇编代码,结果让我很失望,汇编代码完全一样(除 了函数跳转的绝对地址,但相对地址是一样的) ,这说明不是 DEV_DelayMs 函数的问题, 反汇编的代码如下:正常的代码:8019004:f241 736emovwr3, #5998; 0x176e 8019008:fb00 f203mul.wr2, r0,r3 801900c:f04f 0300mov.wr3, #0 8019010:4619mov r1, r3 8019012:bf00nop 8019014:4291cmp r1, r2 8019016:d100bne.n801901a 8019018:e003b.n 8019022 801901a:f101 0301add.wr3, r1,#1 801901e:4619mov r1, r3 8019020:e7f8b.n 8019014 8019022:bf00nop 8019024:4770bxlr异常的代码:8019000:f241 736emovwr3, #5998; 0x176e 8019004:fb00 f203mul.wr2, r0,r3 8019008:f04f 0300mov.wr3, #0 801900c:4619mov r1, r3 801900e:bf00nop 8019010:4291cmp r1, r2 8019012:d100bne.n8019016 8019014:e003b.n 801901e 8019016:f101 0301add.wr3, r1,#1 801901a:4619mov r1, r3 801901c:e7f8b.n 8019010 801901e:bf00nop 8019020:4770bxlrmindows 在运行 DEV_DelayMs 函数的同时还会产生 tick 中断,会不会是 tick 中断对 DEV_DelayMs函数产生了影响?为了验证这个问题, 只需要在 wanlix 上运行 DEV_DelayMs函数就可以了,因为 wanlix 是主动切换任务的系统,如果没有调用任务切换函数,那么 DEV_DelayMs 函数就会一直运行,与没有使用操作系统的情况是一样的。在 wanlix 上运行 DEV_DelayMs 函数,对比钟表的时间,这个问题与 mindows上表现的情况一模一样,依然 存在,说明也不是 tick 中断的问题。 上述 2 个原因都被排除了, 我能想到的最后一个原因就是硬件时钟不准确, 导致芯 片运行的频率错误,最终导致了 DEV_DelayMs 函数执行时间不确定。但 tick 时钟和串口打 印时钟表现出都是正确的情况, 使得这个问题看起来又不像是芯片时钟的问题。 我使用的是 芯片自带的库函数来设置 tick 和串口, 也许库函数里做了一些自适应功能使 tick 和串口时钟 在错误的芯片时钟下也能计算出它们正确的工作时钟?死马当活马医吧, 先看看时钟是否有 问题。查看正常和异常情况下与 tick 时钟有关的寄存器,结果完全正常,这说明芯片时钟也 是没有问题的。而且,异常情况的出现是与代码是否重新编译相关的,与芯片是否重新启动 是无关的,又是概率性出现的,这也说明了不是硬件时钟的问题。这下我真的黔驴技穷了。 经过反复试验, 终于又发现一个重要的线索: 增量编译其它函数时也会触发这个问题概 率性出现。增量编译的意思是只编译其中一部分文件,然后一起连接成目标程序。比如说有 a.c和 b.c这两个文件,先将这两个文件编译成 a.o 和 b.o,然后再将 a.o 和 b.o 链接成目标文 件。当只有 a.c 文件做了改动时,那么我们可以只编译 a.c,使用新生成的 a.o 与原来的 b.o 一起链接成新的目标文件,这就是增量编译。 DEV_DelayMs 函数位于 unoptimize.c 文件中,当我改 动其它 c 文件而没有改动 unoptimize.c 文件做增量编译时,这个问题也会概率性出现。这说明什么?这说明这个问题 与 DEV_DelayMs 函数无关,是其它函数导致的。但这个问题又确实是在 DEV_DelayMs 函 数上表现出来的,而 DEV_DelayMs 函数又与其它函数没有任何耦合,这似乎是不可能的事 情。问题定位到这里已经走不下去了。 既然是修改其它文件里的函数对 DEV_DelayMs 函数有影响,那么只能试着改其它文件 中的函数来找出其中规律了。 在试验中又发现, 在不相关的文件中随便加入几条无用的指令 都有可能随机触发这个问题,这似乎又是不可能的事情。 问题说到这里,我已经把当时定位时所获得的全部信息都介绍全了,现在,你是否能猜 到这个问题的原因?下面就是见证奇迹的时刻! 在不相干的文件中修改不相干的指令,那么对 DEV_DelayMs 函数的影响是链接后 的绝对地址不同。难道是 DEV_DelayMs 函数所在的地址会对运行结果产生影响?带着最后 一丝希望,我构造出了将 DEV_DelayMs 函数链接到不同地址的多种情况,最后终于发现了 导致这个问题的一个规律。 当 DEV_DelayMs 函数被编译到 8 字节对齐(0b1000 结尾)的地址,运行时间就是错误 的,当被编译到 4 字节对齐(0b100 结尾)的地址,运行时间就是正确的! 原文定位到这里就结束了,至于为什么会产生这个问题,我也不清楚,原以为是芯片的 bug。后来在论坛上有网友提出是 FLASH 读取速度不均衡造成的,我觉得有可能,但如果 是这样的话那只能说这个芯片做的不是很好, 因为这个现象是与地址相关的, 而且表现出来 的速度差异太大了,达到了 1:0.8,并且在 TI 的 cortex 内核上没有发现这个问题。因此我 查了一下 ST的 FLASH 手册,发现有如下这段话: “预取缓冲器包含两个数据块,每个数据 块有 8 个字节; 预取指令(数据)块直接映像到闪存中, 因为数据块的大小与闪存的宽度相同, 所以读取预取指令块可以在一个读周期完成。 设置预取缓冲器可以使 CPU 更快地执行, CPU 读取一个字的同时下一个字已经在预取缓冲器中等候,即当代码跳转的边界为 8 字节的倍 数时,闪存的加速比例为 2。 ”从中可以看到其中提到了“跳转” ,提到了“8 字节对齐” , 从上面的 2 段反汇编代码可以看到, 区别就在于跳转的地址不同, 并且我验证的结论也是有 关 8 字节对齐的, 因此这段话与我上面所得出的结论是一致的。 至于上面 2 段反汇编后的代码为何会表现出指令执行速度上的差异,我结合这段话没有找到原因。解决方法解决方法经过前面的分析,我们需要将 DEV_DelayMs 函数链接到 4 字节对齐的地址空间,需要 修改链接文件,在我的工程中这个链接文件名称叫 STM3210E-EVAL.sct,你可以到我的博 客 mindows5.3 节及以后的章节中找到这部分代码,修改如下:LR_IROM2 0x08019004 0x00000FFC; load region size_region ER_IROM2 0x08019004 0x00000FFC; load address = execution address unoptimize.o 上面这段代码的含义是,将 unoptimize.o 文件链接到 0x08019004 这个地址空间,这就 是一个 4 字节对齐的地址空间,而 unoptimize.o 是由 unoptimize.c 文件编译成的,而 unoptimize.c 文件中只有 DEV_DelayMs 这一个函数, 这样就将 DEV_DelayMs 函数链接到了 4 字节对齐的 0x08019004 地址空间了。 经过这么处理 DEV_DelayMs 函数就可以正常工作了。另外,在 ST 的 FLASH 手册中也指出了可以通过寄存器关
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号