• 在Cortex-M系列上如何准确地做us级延时?


    前几天刚好同事问起在Cortex-M上延时不准的问题,在网上也没找到比较满意的答案,干脆自己对这个问题做一个总结。
    根据我们的经验,最容易想到的大概通过计算指令周期来解决。该思路在Cortex上并不是很适用:一方面MCU从Flash取指是有延时的,另一方面Cortex的指令集不是固定周期的,特别从M3加入分支预测后,分支指令在Cortex-M不同型号上的结果都不相同。因此除了指令周期外,我们需要考虑的东西还有很多,才能得到正确的结果。

    不带分支预测器的情况

    仍然先从不带分支预测器的Cortex-M0开始,通过计算指令周期延时的实现代码如下:

    void delay_us(us) {
         delay_ntimes((us * sysclk - 8) / 4);
    }
    __asm void delay_ntimes(unsigned int n)
    {
    L1
                   SUBS R0, #1
                   BCS L1
                   BX LR
    }
    

    从这段代码可发现两个主要问题:
    一、delay_us里的公式是怎么来的:
    假如想延时us微秒,系统时钟为48MHz,即sysclk=48,那么周期数period_count满足以下公式:
    period_count = us * sysclk;
    然后再delay_ntimes这个函数,又能推出period_count还满足以下公式(见第二个问题的分析):
    period_count = 8 + 4 *n
    于是:
    n = (us * sysclk - 8 ) / 4;
    这就解决了第一个问题,需要注意的是:该公式忽略了跳转到delay_us和(us * sysclk -8 )/4的几个固定周期。
    二、delay_ntimes的周期数怎么算:
    它的周期数满足以下公式:
    period_count = 8 + 4 * n;
    这个要根据指令集的周期数来确定,请看下表:

    操作 描述 汇编命令 周期
    Subtract Lo to Lo SUBS Rd,Rn,Rm 1
    3-bit immediate SUBS Rd,Rn,#<imm> 1
    8-bit immediate SUBS Rd,Rd,#<imm> 1
    Branch Conditional B<cc> <label> 1或3
    Unconditional B<label> 3
    With link BL<label> 4
    With exchange BX Rm 3
    With link and exchange BLX Rm 3

    先考虑n为0的情况,
    SUBS为1周期+BCS为1周期+BX为3周期+外层调用delay_times(相当于BLX指令)的3周期=8周期。
    当n不为0时,将再执行n次SUBS和BCS执行,SUBS仍为1周期,BCS有跳转3周期,所以是4n个周期,因此该函数的执行周期数为:
    period_count=8+4
    n;

    好了,在了解了原理之后,是时候到真正的板子上去测试了。
    然而在MCU上的实测结果却不如预期,延时5MS,实测为7.5MS;延时10MS,实测15MS。为什么会出现这样的现象?

    这个跟MCU的设计有关。一般代码都放在FLASH上,MCU中Cortex核要从FLASH上先取出指令,然后才能将指令放到指令流水线上执行。而上面的分析忽略了Cortex核从FLASH取出指令的时间,因此实测值与理论值分析不一致。

    不同的MCU从FLASH读取指令的时间消耗各不相同,因此需要根据不同MCU去调整公式,这是一个比较繁琐的过程,比如这款MCU,将公式修改为(us * sysclk - 8) / 6就得到了正确结果。
    另外一个做法是不修改公式,将延时代码放到RAM中,许多MCU从RAM取出指令没有等待周期。使用该方法再次测试,延时结果与理论计算一致。
    但值得注意的是,不是所有MCU都满足RAM取值零等待周期的条件,因此一定要做测试。

    读者若对MCU如何从FLASH读取指令感兴趣,参考资料[4]的分析是比较清楚的。

    带分支预测器的情况

    将上面的代码放到Cortex-M3和Cortex-M4的芯片上测试,测试结果是错误的,不论在FLASH还是在RAM中,这个是由于Cortex-M3,Cortex-M4上的指令流水线带有分支预测器引起的。

    要了解分支预测器,就不得不提指令流水线。Cortex-M3是三级流水线:取指,解码,执行。但是没找到CORTEX方面较好的图,以下讨论就基于下图的4级流水线,该图多了一步:写回。这并不影响我们的讨论。
    流水线

    (该图引用自参考资料[1])
    假设一条指令从执行开始到执行结束需要4个时钟周期,在没有流水线的情况下,需要等待第一条指令执行结束,才能取第二条指令,这时两条指令就用了8个周期,效率是很低的。
    引入4级流水线将指令拆成4个步骤:取指、解码、执行、写回。当第一条指令处于解码时,同时对第二条指令取指;对第一条指令执行时,同时对第二条指令解码,对第三条指令取指;对第一条指令写回时,同时对第二条执行,第三条解码,第四条取指;如此这般。最终达到的效果就如上图所示,只有第一条指令需要4个周期,其他后续的指令都只需要1个周期,极大地提高了处理效率。
    流水线的高效率是基于指令顺序执行的前提,在执行跳转指令时,流水线将被清空,又回到了上图中的第一步,跳转后的第一条指令要执行仍然需要4周期。因此如果程序频繁跳转,流水线的作用就大打折扣。
    为了解决这个问题,就引入了分支预测器:它会提前检测到跳转指令,并根据预判结果取指。如果预判结果是不跳转,就按顺序取下一条指令;如果预判结果是跳转,就从跳转的目的地址取下一条指令。假如预测对了,那么流水线就不会被清空,仍然可以一条指令1个周期;如果预测错了,下一条指令仍然要4周期。从这里看出,分支预测器对于提高流水线效率是有帮助的。值得一提的是,预判对了能减少指令延迟,但是否是零延迟取决于MCU的设计;预判错了清空流水线也未必是唯一的做法,同样取决于MCU的设计。

    回到Cortex-M3的延时问题,网络上找到的资料提到分支预测器将延迟减小到1个周期,没有找到更详细的说明。那么理论上计算公式就应该调整为(us * sysclk - 8) / 3,在两款Cortex-M3和两款Cortex-M4上测试,测试结果与理论值一致。

    微秒级精确延时的其他方法

    对于Cortex而微秒级延时最通用的方法,大概便是通过比较SysTick的SYST_CVR寄存器来做延时,理论误差在1us内(基于48MHz主频)。以下为实现代码:

    /*
    * 使用SysTick的CVR实现微秒级精确延时,一般SysTick周期设置为10MS,因此该方法适用于10MS以内的延时
    */
    void delay_us(int us) {
    	unsigned t1, t2, count, delta, sysclk;
    	sysclk = 48;//假设为48MHz主频
    
    	t1 = SYST_CVR;
    	while (1) {
    		t2 = SYST_CVR;
    		delta = t2 < t1 ? (t1 - t2) : (SYST_RVR - t2 + t1) ;
    		if (delta >= us * sysclk)
    			break;
    	}
    }
    

    其他补充点

    1. 本文假设在延时过程中没有产生任何中断,如果有中断产生,将影响延时精确性。
    2. 这部分的内容属于计算机体系结构。
    3. 以上测试时间范围在[0,10MS),该范围之外未详细测试,建议采用其他方法。
    4. 覆盖测试的MCU:1款Cortex-M0,2款Cortex-M3,2款Cortex-M4。
    5. 在我测试的两款Cortex-M3 MCU上,将代码都放RAM上,测试结果比放在FLASH差,而在Cortex-M4 MCU上,测试结果都一样,目前没有找到合理的解释。

    参考资料

    1. 浅谈分支预测、流水线与条件转移
    2. Cortex-M0指令集
    3. CPU性能衡量参数-主频,MIPS,CPI,时钟周期,机器周期,指令周期
    4. Cortex-M3的周期判断的依据是什么
    5. 计算机体系结构——流水线中的相关——延迟分支方法
  • 相关阅读:
    Linux 配置jdk vim和 Linux 基本操作
    Java02_数据类型
    java01_简介_开发环境
    基于Vue + webpack + Vue-cli 实现分环境打包项目
    理解TCP/IP三次握手与四次挥手的正确姿势
    Vue 项目骨架屏注入与实践
    我的第一个Quartz代码
    hdu5882 Balanced Game
    hdu5883 The Best Path(欧拉路)
    Poj 1273 Drainage Ditches(最大流 Edmonds-Karp )
  • 原文地址:https://www.cnblogs.com/pheye/p/5630938.html
Copyright © 2020-2023  润新知