i.MXRT的PIT定时器及其在Coremark测试工程里的应用
来源:恩智浦MCU加油站 发布时间:2022-03-24 分享至微信

早在2018年i.MX RT系列跨界处理器刚推出的时候,我就写了一篇 《i.MX RT1052性能实测(CoreMark)》,文章详细介绍了在i.MX RT上如何一步一步地移植标准coremark 程序,这篇文章阅读量还不错,据说很多人移植coremark都是看的这篇文章。

当时痞子衡把移植好的coremark工程也一起开源了出来,并且这个仓库痞子衡也是在不断维护的(增加新MCU型号支持,以及除了coremark之外的一些其他经典程序)。
最近有同事向痞子衡反映,这个coremark测试工程里关于计时部分有一些可以改进的地方,痞子衡看了一下,确实可以改进,这便是今天本文要聊的主题:

开源地址:https://github.com/JayHeng/cortex-m-apps


一、i.MX RT上的定时器简介


工欲善其事,必先利其器。在改进coremark测试工程里计时功能之前,我们先来了解一下i.MX RT上都有哪些跟计时/计数相关的模块,分别是什么特点,下面是详细列表。


单从计时功能角度考虑,SysTick、GPT、PIT、TMR都是不错的选择。



二、计时对coremark测试的影响


我们知道coremark标准的测试逻辑,是在某配置参数组合下单位时间内跑了多少次coremark程序,一般情况下要求至少跑10s以上,因此计时部分的设计是很重要的。


在早期i.MX RT1050 coremark工程里,我选用了PIT(channel 0 - 32bit)负责计时,为PIT配置的时钟源是24MHz外部OSC,定时器一次超时耗时约178s,这种情况下,没有使能PIT中断,假定了一次coremark程序跑完不会碰到超时的情况,但显然这种设计是不完善的。


此外我们知道定时器时钟源频率越高,计时粒度越细,计时时间也就越精确。大部分定时器时钟源都可以配到系统IPG bus总线频率(在i.MX RT10xx上可达125MHz/150MHz,在i.MX RT1170上可达240MHz),我们可以尝试将定时器设到最高频率的时钟源,这时候就不得不考虑定时器超时中断处理问题了。


使能定时器超时中断,可以保证计时的严谨性,解决了coremark程序运行时间和次数的限制。但是频繁的定时器中断响应也会不断打断coremark程序的执行,对最终跑分结果产生不利影响,这个问题同样需要解决。


三、PIT定时器多通道链接模式


前面介绍过SysTick、GPT、PIT、TMR都可以用作coremark测试工程定时器,但最终还是选定了PIT,因为PIT是最适合作为系统运行生命周期总计时器的,这主要得益于PIT内部有4个32bit计时器,并且可以链接使用(串连)。


要是将4个32bit计数器串成一个128bit超强计数器(channel 0计数溢出,channel 1 计数加 1...),即使系统运行到地老天荒都不会出现一次超时(这里指最后一链channel3中断触发),所以也就根本不用管定时器中断处理的事。


PIT通道链接模式使能也很简单,需要配置PIT->CHANNEL[x].TCTRL[CHN]位,这个位开启后,channel x就和channel x-1连了起来。下面是channel 0和channel 1串连组成 64bit计数器的初始化代码:


void timer_pit_init(void)
{
// Turn on PIT: MDIS = 0, FRZ = 0
PIT->MCR = 0x00;

// Set up timer 1 to max value
PIT->CHANNEL[1].LDVAL = 0xFFFFFFFF;
// setup timer 1 for maximum counting period
PIT->CHANNEL[1].TCTRL = 0;
// Disable timer 1 interrupts
PIT->CHANNEL[1].TFLG = 1;
// clear the timer 1 flag
PIT->CHANNEL[1].TCTRL |= PIT_TCTRL_CHN_MASK;
// chain timer 1 to timer 0
PIT->CHANNEL[1].TCTRL |= PIT_TCTRL_TEN_MASK;
// start timer 1

// Set up timer 0 to max value
PIT->CHANNEL[0].LDVAL = 0xFFFFFFFF;
// setup timer 0 for maximum counting period
PIT->CHANNEL[0].TFLG = 1;
// clear the timer 0 flag
PIT->CHANNEL[0].TCTRL = PIT_TCTRL_TEN_MASK;
// start timer 0
}


实际上我们也根本不需要128bit计数器,64bit计数器就完全够用了,就以150MHz时钟源来说,超时一次需要约3899年,谁需要操心3899年后的事情呢?


此外,在channel 0和channel 1串联的情况下,PIT还提供了一个64bit lifetime计数器,直接读这个计数器就能获取当前channel0,1串连的计数值,不用考虑手动读channel 0,1计数值可能会发生的潜在翻转问题(rollover)。


你看,使能了PIT通道链接用法后,可以完美解决coremark测试程序计时设计问题。


uint64_t timer_pit_get_ticks() {
uint64_t valueH;
volatile uint32_t valueL;


#if defined(FSL_FEATURE_PIT_HAS_LIFETIME_TIMER) && (FSL_FEATURE_PIT_HAS_LIFETIME_TIMER == 1)
valueH = PIT->LTMR64H;
valueL = PIT->LTMR64L;

#else
do
{
valueL = PIT->CHANNEL[0].CVAL;
valueH = PIT->CHANNEL[1].CVAL;
} while (valueL < PIT->CHANNEL[0].CVAL);

#endif // FSL_FEATURE_PIT_HAS_LIFETIME_TIMER

return ~((valueH << 32) | valueL);
}


四、coremark计时的其他改进点


最后再提两个coremark测试程序设计小改进点,一是在一些双核型号上(比如i.MX RT1170, CM7和CM4),如果两个核同时跑coremark程序要用到不同PIT的话,需要检查它们是不是共用一个时钟开关,防止出现CM7上跑完了coremark之后关掉PIT,影响CM4那边coremark程序对PIT寄存器的访问。


第二个改进点是core_main.c里的main()函数,在打印Total ticks时会将u64型的total_time变量强制转为u32,以便于%lu格式化输出(32位无符号整数),这里最好还是保留原来u64精度;尝试过%llu格式化输出(64位无符号整数),结果在ee_printf()下不生效,所以做了个如下手动转换版:


MAIN_RETURN_TYPE main(void) {
// 代码省略...
uint64_t total_time;

total_time=get_time();

//ee_printf("Total ticks : %lu\n",(ee_u32)total_time);
if (total_time & (~(uint64_t)0xFFFFFFFF))
{
ee_printf("Total ticks : ");
ee_printf("%lu", (ee_u32)(total_time / 1000000000));
ee_printf("%lu\n",(ee_u32)(total_time % 1000000000));
}
else
{
ee_printf("Total ticks : %lu\n",(ee_u32)total_time);
}

// 代码省略...
}



[ 新闻来源:恩智浦MCU加油站,更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj]
存入云盘 收藏
举报
全部评论

暂无评论哦,快来评论一下吧!