cuter

【再话Zedboard】如何在SDK中计算某段程序的执行时间

0
阅读(13185)

首先赞一下自动保存功能,今天在网页上写的,不小心关掉了,那个心疼啊,幸好有自动保存功能,成功恢复了!

废话不多说了,直奔主题吧。

计算一段程序的执行时间主要是为了方便计算一些算法的效率,当然,如果能够计算出一段程序的执行时间,也就能够轻松编写出精确延时时间了。

调试51单片机的时候,可以可以在Keil中设定断点,直观地计算出两个断点之间的程序运行时间,也可以利用反汇编代码计算某段程序的运行时间。

对于zynq的SDK而言,第一种方法无法实现;第二种方法虽然可行,但对于较长的程序段,计算起来是很麻烦的,也不太可取。

思来想去,也有两种比较可行的方法:

1、利用一个GPIO指示程序运行的开始和结束,例如程序开始前和结束后将GPIO置低,程序运行过程中,将GPIO拉高,用示波器测量高电平持续时间。

2、获取某程序段执行前程序运行的机器周期总数N1,该程序段执行后程序运行的机器周期总数N2,就可以计算出该段程序的执行时间T,T=(N2-N1)*机器周期。

第一种方法就不多说了,比较简单,缺点是需要外界测量设备配合。

第二种方法可以利用ARM 内核中的Performance Monitor Unit(PMU,性能检测单元)实现。

下面首先给出示例代码,然后再分析实现过程。

#include  #include "sleep.h" #include "xil_io.h" #include "xtime_l.h" #include "xil_printf.h" #include "xpm_counter.h" #include "xparameters.h" #define COUNTS_PER_SECOND (XPAR_CPU_CORTEXA9_CORE_CLOCK_FREQ_HZ / 64) int main() { XTime tEnd, tCur; u32 tUsed; XTime_GetTime(&tCur); usleep(1345); XTime_GetTime(&tEnd); tUsed = ((tEnd-tCur)*1000000)/(COUNTS_PER_SECOND); printf("time elapsed is %d us\r\n",tUsed); while(1); //等待 return 0; }


调试结果:

本例测量误差为:3.57%。对于多个程序段的测量后发现,误差具有随机性,例如,延迟45us,测量值为52us,误差约为16%;延时13045us,测量值为12533us,误差约为3.92%。

虽然存在一定的误差,也差强人意了,精度问题需要进一步研究。

下面,分析一下具体代码:

上面代码关键点有两条:

1、XTime_GetTime()函数如何实现;

2、公式tUsed = ((tEnd-tCur)*1000000)/(COUNTS_PER_SECOND)的含义;

1、XTime_GetTime()的代码如下:

/**************************************************************************** * * Get the time from the Cycle Counter Register. * * @param Pointer to the location to be updated with the time. * * @return None. * * @note None. * ****************************************************************************/ void XTime_GetTime(XTime *Xtime) { u32 reg; u32 low; /* loop until we got a consistent result */ do { #ifdef __GNUC__ low = mfcp(XREG_CP15_PERF_CYCLE_COUNTER); reg = mfcp(XREG_CP15_V_FLAG_STATUS); #else { register unsigned int Reg __asm(XREG_CP15_PERF_CYCLE_COUNTER); low = Reg; } { register unsigned int Reg __asm(XREG_CP15_V_FLAG_STATUS); reg = Reg; } #endif if (reg & CYCLE_COUNTER_MASK) { /* clear overflow */ mtcp(XREG_CP15_V_FLAG_STATUS, CYCLE_COUNTER_MASK); high++; } } while (reg & CYCLE_COUNTER_MASK); *Xtime = (((XTime) high) << 32) | (XTime) low; }


其中:

#define XREG_CP15_PERF_CYCLE_COUNTER "cp15:0:c9:c13:0"
#define XREG_CP15_V_FLAG_STATUS "cp15:0:c9:c12:3"

调试的时候,由于定义了 #define __GNUC__的情况下,执行

#ifdef __GNUC__ 和 #else之间的代码。

mfcp指令定义为:

#define mfcp(rn) ({unsigned int rval; \ __asm__ __volatile__(\ "mrc " rn "\n"\ : "=r" (rval)\ );\ rval;\ })

mrc指令如下:


这里纠结啊,找了这么多资料,还是看不明白,最后反汇编结果如下:

上述指令操作的是arm CP15协处理器的 c9 寄存器(performance monitors)。

mrcp15, 0, r12, cr9, cr13, {0};将CP15的寄存器C9的值读到r12中,由下图可以看出该语句是读取PMCCNTR寄存器

mrcp15, 0, r3, cr9, cr12, {3};将CP15的寄存器C9的值读到r3中,由下图可以看出该语句是读取PMOVSR寄存器
至此,XTime_GetTime()函数解析得差不多了,不过多少还是有点不清晰,希望在行的网友能给我一些帮助~

最终读取的是PMCCNTR,也就是Performance Monitors Cycle Count Register的值。接下来看一下PMCCNTR:

由上图可以看出,计数周期可能存在两个值:i)、PMCR.D=0,每个时钟周期计数一次 ii)、PMCR.D = 1时,每64个时钟周期计数一次。

时间计算公式应根据这两种情况确定,我这里用的公式是针对的第ii种情况。

至于系统什么时候给PMCR.D赋值,明天再分析吧,今天有点晚了~

版权声明:

本文由博主“cuter”发布。欢迎转载,但不得擅自更改博文内容,也不得用于任何盈利目的。转载时不得删除作者简介和版权声明。如有盗用而不说明出处引起的版权纠纷,由盗用者自负。

博客官方地址:

ChinaAET:http://blog.chinaaet.com/cuter521

EDN China:http://bbs.ednchina.com/BLOG_cuter521_356737.HTM

Baidu
map