URL: http://blog.chinaaet.com/detail/31789
首先赞一下自动保存功能,今天在网页上写的,不小心关掉了,那个心疼啊,幸好有自动保存功能,成功恢复了!
废话不多说了,直奔主题吧。
计算一段程序的执行时间主要是为了方便计算一些算法的效率,当然,如果能够计算出一段程序的执行时间,也就能够轻松编写出精确延时时间了。
调试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)
intmain()
{
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); //等待
return0;
}
调试结果:
/****************************************************************************
*
* 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 mfcp(rn) ({unsigned int rval; \
__asm__ __volatile__(\
"mrc " rn "\n"\
: "=r" (rval)\
);\
rval;\
})
mfcp指令如下:
最终读取的是PMCCNTR,也就是Performance Monitors Cycle Count Register的值。接下来看一下PMCCNTR:
由上图可以看出,计数周期可能存在两个值:i)、PMCR.D = 0,每个时钟周期计数一次 ii)、PMCR.D = 1时,每64个时钟周期计数一次。
时间计算公式应根据这两种情况确定,我这里用的公式是针对的第ii种情况。
至于系统什么时候给PMCR.D赋值,明天再分析吧,今天有点晚了~
关于PMCR.D=1的赋值语句,我在这简单给一下,就不再新开一篇了:
具体赋值是在standalone_bsp提供的启动代码,对cpu初始化时进行的,具体代码为:
mov r2, #0xd /* D, C, E */
mcr p15, 0, r2, c9, c12, 0
mov r2, #0x80000000 /* enable cycle counter */
mcr p15, 0, r2, c9, c12, 1
具体意思就不赘述了,文章中已经给出了相关参考资料。该段代码位于cpu_init.s中。
以上引用了cuter在ChinaAET的博客,参照其代码自建了工程,非常感谢。这里补一些自己的说明。
硬件设施:ZedBoard
开发环境:Xilinx Design Tools 14.4
将cuter的源码导入工程后,得出的结果误差很大。后来发现是XTime_GetTime的实现方式不同。
/****************************************************************************
*
* Get the time from the Global Timer Counter Register.
*
* @param Pointer to the location to be updated with the time.
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XTime_GetTime(XTime *Xtime)
{
u32 low;
u32 high;
/* Reading Global Timer Counter Register */
do
{
high = Xil_In32(GLOBAL_TMR_BASEADDR + GTIMER_COUNTER_UPPER_OFFSET);
low = Xil_In32(GLOBAL_TMR_BASEADDR + GTIMER_COUNTER_LOWER_OFFSET);
} while(Xil_In32(GLOBAL_TMR_BASEADDR + GTIMER_COUNTER_UPPER_OFFSET) != high);
*Xtime = (((XTime) high) << 32) | (XTime) low;
}
看的出,这里是由Cortex-A9的全局定时器读取的计数值,该定时器使用的是CPU时钟的二分频。所以更改后的代码为:
/*
* PMU_Demo.c
*
* Created on: 2014-7-14
* Author: Administrator
*/
#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 / 2)
int main()
{
XTime tEnd, tCur;
u32 tUsed;
XTime_GetTime(&tCur);
usleep(1345);
XTime_GetTime(&tEnd);
tUsed = ((tEnd-tCur)*1000000)/(COUNTS_PER_SECOND);
xil_printf("time elapsed is %d us\r\n",tUsed);
while(1); //等待
return 0;
}
此时得出的时间误差就非常小了。