搜索
bottom↓
回复: 17

求ARM汇编的高手给个delay函数

[复制链接]

出0入0汤圆

发表于 2016-8-7 14:55:08 | 显示全部楼层 |阅读模式
如题,在linux下用gcc调试cortex-m0,发现while(i--)全被编译器给优化掉了,i前面加了volatile也不管用。
想用系统的systick寄存器做,老板居然说你自己的模块里最好别调用这个,那么只好另想办法。
gcc只会拿来编译,高级用法不会,例如关优化。
突然想到我之前调试mips的核可以用段asm指令插到c代码之中,asm指令是不会被优化掉了,可惜刚从mips转到arm的核,arm的汇编指令还没入门,特向广大坛求伸手求之
格式如下,可以将delay参数通过指令中的%0参数传递到汇编指令中的通用寄存器,ins0~insn为汇编指令,通过汇编指令对delay参数进行自减到0退出。
另外我在mips的汇编中需要加入.set noreorder伪指令,否则即是便汇编代码也会被优化,不知arm是否也有对应的这样一条伪指令


uint32_t delay;
__asm volatile(
"ins0      \n"
"ins1      \n"
"ins2      \n"
"ins3      \n"
...
:
:r"(delay)
);


另外我编译用的工具为arm-none-eabi-gcc,不知是否有精通此工具的坛友给个更简便的方法,如在程序中加优化开关。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2016-8-7 15:12:24 | 显示全部楼层
基本上你用 asm 加上 volatile 就可以了。
至于那个汇编指令可以是空。
gcc 并不能理解 asm 里面有什么汇编代码。
因为有 volatile,你相当于告诉 gcc 这段 asm 有
side effect,gcc 不能优化那段 asm 指令。
需要执行足够多的次数。

gcc 仍然可以移动 asm volatile 的代码块的次序。
在这里应该没有什么影响。

        int i = delay_count;
        while (--i > 0)
                __asm__  volatile ("");

出0入0汤圆

 楼主| 发表于 2016-8-7 16:17:42 | 显示全部楼层
不好意思是我自己弄错了,while(i--)对i加volatile修饰是好使的,我在调用这个delay函数时不知道碰到什么鬼居然注释掉了,导致我怎么修改delay函数内部都无法看到有变化,因为此函数根本就没被调用。

出0入0汤圆

发表于 2016-8-8 00:22:55 | 显示全部楼层
ARM的DWT延时,听说精确好用,还不占用任何定时器,不过要注意溢出翻转

出0入0汤圆

发表于 2016-8-8 01:39:18 | 显示全部楼层
Achin 发表于 2016-8-8 00:22
ARM的DWT延时,听说精确好用,还不占用任何定时器,不过要注意溢出翻转

DWT 的确有意思。不过 M0 这个级别不一定有, DWT 在 M3 被定义的。

出0入0汤圆

发表于 2016-8-8 08:43:43 | 显示全部楼层
helislayer 发表于 2016-8-8 01:39
DWT 的确有意思。不过 M0 这个级别不一定有, DWT 在 M3 被定义的。

DWT之前只在CM4内核上用过,发帖之前也没有确认一下CM0上是否存在。不过刚去查了一下CM0的技术参考手册,也是有DWT的。

出0入0汤圆

发表于 2016-8-8 09:03:30 | 显示全部楼层
Achin 发表于 2016-8-8 08:43
DWT之前只在CM4内核上用过,发帖之前也没有确认一下CM0上是否存在。不过刚去查了一下CM0的技术参考手册, ...

我手上是ARM的文档,显示DWT默认“User access results in bus fault.” 求解?

出0入0汤圆

发表于 2016-8-8 09:18:40 | 显示全部楼层
DWT 用过。但是出现死机现象。停在while上了。不敢用了。

出0入0汤圆

发表于 2016-8-8 15:02:48 | 显示全部楼层
Achin 发表于 2016-8-8 08:43
DWT之前只在CM4内核上用过,发帖之前也没有确认一下CM0上是否存在。不过刚去查了一下CM0的技术参考手册, ...

你是在看那个 CM0 有 DWT 的?
我的是 ARM 的官方文档,在 M0 和 M0+ 的 technical reference manual 是没有的。
然后 ST 的 STM32F0xxx Cortex-M0 programming manual 里面汇编指令也没有提到
DWT。在这个 pdf 里面寻找 DWT 完全就没有找到。

出0入0汤圆

发表于 2016-8-8 17:38:12 | 显示全部楼层
大豆皮 发表于 2016-8-8 09:18
DWT 用过。但是出现死机现象。停在while上了。不敢用了。

while那个地方,要注意数值溢出的情况。不然可能会有一个极长的等待,而且用这个东西,还不能被打断,缺陷是有的

出0入0汤圆

发表于 2016-8-8 17:40:14 | 显示全部楼层
helislayer 发表于 2016-8-8 15:02
你是在看那个 CM0 有 DWT 的?
我的是 ARM 的官方文档,在 M0 和 M0+ 的 technical reference manual 是 ...

ARM官方网站的。链接http://infocenter.arm.com/help/i ... 0432c/BCGGAEGA.html

出0入0汤圆

发表于 2016-8-8 17:44:56 | 显示全部楼层
Achin 发表于 2016-8-8 17:40
ARM官方网站的。链接http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0432c/BCGGAEGA.h ...

啊多谢指出,我发现我原来以为 DWT 是个汇编指令是错的。
DWT 是个外部寄存器。 M0 手册放的位置和 M3 不一样所以我
没有找到。

多谢啦。看样子 M0 也应该是有的,就是要用 DWT 的时候要
先测一下是否提供那个功能,例如时钟计数。

出0入0汤圆

发表于 2016-9-5 17:16:46 | 显示全部楼层
您好,我有个汇编的程序,您可以帮忙写吗?我微信CNOMEGA

出0入0汤圆

发表于 2016-9-5 19:45:31 | 显示全部楼层
跑系统的delay靠不住啊,当然只会长于给定值。最坏状况可能多跑一个系统调用的时间片,可能是10-20ms

出0入0汤圆

发表于 2016-9-5 21:23:11 来自手机 | 显示全部楼层
Achin 发表于 2016-8-8 00:22
ARM的DWT延时,听说精确好用,还不占用任何定时器,不过要注意溢出翻转

dwt是在哪里

出0入0汤圆

发表于 2016-9-6 08:25:05 | 显示全部楼层
给一个我用的吧
#define SYSTEM_CORE_CLOCK 108000000UL
#define SYS_100NS (SYSTEM_CORE_CLOCK/1000)/(3*1000*10)+1
#define SYS_1US   (SYSTEM_CORE_CLOCK/1000)/(3*1000)
#define SYS_1MS   (SYSTEM_CORE_CLOCK/1000)/3

__asm void tmr_sysCtlDelay(unsigned long ulCount)
{
    subs    r0, #1;
    bne     tmr_sysCtlDelay;
    bx      lr;
}

出0入8汤圆

发表于 2016-9-6 09:04:57 | 显示全部楼层
zhugean 发表于 2016-9-6 08:25
给一个我用的吧
#define SYSTEM_CORE_CLOCK 108000000UL
#define SYS_100NS (SYSTEM_CORE_CLOCK/1000)/(3*1 ...

关注下 dwt。

但如果纯粹是软件延时,不要求很精确的话,直接用 @zhugean 这种类似的方法就好了。
下面,我就多嘴说一些,望见谅.

这种方法从本质上来说,跟 51 单片机时代的计算软件延时的算法一致:
只需要计算出一次 loop 的指令周期就好了。

以 @zhugean 的例子为例:
1、ARM 采用流水线架构,当流水线欢快的跑起来后,相当于每个时钟周期,都有一条指令在执行;
2、因此,可以简单的认为指令周期,等同于时钟周期;
3、loop 主要包括如下语句:
  1. __asm void tmr_sysCtlDelay(unsigned long ulCount)
  2. {
  3.     subs    r0, #1;
  4.     bne     tmr_sysCtlDelay;
  5. }
复制代码
,其中 subs    r0, #1; 占用 1 个周期,bne     tmr_sysCtlDelay;也占用 1个 周期;
4、但是 bne 属于跳转指令,如果 r0 还未减到 0 的话,就会发生了跳转,重新跳转到 loop 开头,也就是 subs    r0, #1;
5、此时将导致 bne 流水线下面,紧邻的、正在译码的那条指令(在这边对应为 BX LR 语句),要被丢弃掉,需要重新去取指令,浪费掉一个指令周期;
6、因此一次 loop 的 周期数目,是 3;
7、所以循环 1ms,所需要的 loop 数目是:系统时钟 / 1000 / 3,其他的延时计算方法是类似。

这里我们也可以看出,跳转指令对于流水线的副作用,因此对于追求高效率的优化,有一种思路是:可以考虑舍弃分支跳转逻辑,尽量多用顺序执行逻辑。

出0入0汤圆

发表于 2016-9-6 13:19:26 | 显示全部楼层
从某处get的代码

  1. #include "stm32f30x.h"

  2. //使用DWT的时钟周期计数对延迟进行管理,与delay_systick不可同时使用

  3. #define  DWT_CR                                *(volatile u32 *)0xE0001000
  4. #define  DWT_CYCCNT                        *(volatile u32 *)0xE0001004
  5. #define  DEM_CR                                *(volatile u32 *)0xE000EDFC
  6. #define  DEM_CR_TRCENA                (1 << 24)
  7. #define  DWT_CR_CYCCNTENA        (1 << 0)
复制代码


  1. static u8 cpuclkfeq;     //用于保存cpu运行频率,可运行时动态修改
  2. static u32 startts, endts, wait_ms;

  3. //初始化延时系统,参数为CPU频率
  4. void delay_init(u8 clk)
  5. {
  6.         cpuclkfeq = clk;

  7.         //打开CYCCNT功能,并把计数器清零,最后打开计数器对cpu时钟进行向上计数
  8.         DEM_CR |= DEM_CR_TRCENA;
  9.         DWT_CR |= DWT_CR_CYCCNTENA;
  10. }


  11. //延时函数,参数为需要延时的微秒数
  12. void delay_us(u32 nus)
  13. {
  14.         u32 ts;
  15.         //保存进入函数时的计数器值
  16.         startts = DWT_CYCCNT;
  17.         ts = nus * cpuclkfeq - 8;                        //计算达到所需延时值的cpu时钟数,减去运行前面代码所需的时钟数
  18.         endts = startts + ts;                                //计算达到所需延时时间的DWT_CYCCNT计数值,超过32bit所能表达的最大值2的32次方-1是自动绕回丢弃进位
  19.         if(endts > startts)                                        //判断是否跨越最大值边界
  20.         {
  21.                 while(DWT_CYCCNT < endts);                //等到计数到所需延时值的cpu时钟数值
  22.         }
  23.         else
  24.         {
  25.                 while(DWT_CYCCNT > endts);                //等待跨域32bit的最大值,2的32次方-1
  26.                 while(DWT_CYCCNT < endts);                //等到计数到所需延时值的cpu时钟数值
  27.         }
  28. }


  29. void delay_ms(u32 nms)
  30. {
  31.         u32 ts;
  32.         //保存进入函数时的计数器值
  33.         startts = DWT_CYCCNT;
  34.         ts = 1000 * nms * cpuclkfeq - 10;        //计算达到所需延时值的cpu时钟数,减去运行前面代码所需的时钟数
  35.         endts = startts + ts;                                //计算达到所需延时时间的DWT_CYCCNT计数值,超过32bit所能表达的最大值2的32次方-1是自动绕回丢弃进位
  36.         if(endts > startts)                                        //判断是否跨越最大值边界
  37.         {
  38.                 while(DWT_CYCCNT < endts);                //等到计数到所需延时值的cpu时钟数值
  39.         }
  40.         else
  41.         {
  42.                 while(DWT_CYCCNT > endts);                //等待跨域32bit的最大值,2的32次方-1
  43.                 while(DWT_CYCCNT < endts);                //等到计数到所需延时值的cpu时钟数值
  44.         }
  45. }


  46. void delay_us_set(u32 nus)
  47. {
  48.         u32 ts;
  49.         //保存进入函数时的计数器值
  50.         startts = DWT_CYCCNT;
  51.         ts = nus * cpuclkfeq - 8;                        //计算达到所需延时值的cpu时钟数,减去运行前面代码所需的时钟数
  52.         endts = startts + ts;                                //计算达到所需延时时间的DWT_CYCCNT计数值,超过32bit所能表达的最大值2的32次方-1是自动绕回丢弃进位
  53. }


  54. void delay_ms_set(u32 nms)
  55. {
  56.         u32 ts;
  57.         //保存进入函数时的计数器值
  58.         startts = DWT_CYCCNT;
  59.         ts = 1000 * nms * cpuclkfeq - 10;        //计算达到所需延时值的cpu时钟数,减去运行前面代码所需的时钟数
  60.         endts = startts + ts;                                //计算达到所需延时时间的DWT_CYCCNT计数值,超过32bit所能表达的最大值2的32次方-1是自动绕回丢弃进位
  61. }
复制代码
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 14:31

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表