查看: 1086|回复: 2

[分享] [痞子衡]如何用PIT定时器的多通道链接模式去改善coremark测...

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3300

    主题

    6547

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32030
    最后登录
    2024-4-26
    发表于 2022-3-25 09:47:48 | 显示全部楼层 |阅读模式
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT定时器PIT的多通道链接模式及其在coremark测试里的应用。


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


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

    11.png
    一、i.MXRT上的定时器简介
    工欲善其事,必先利其器。在改进 coremark 测试工程里计时功能之前,我们先来了解一下 i.MXRT 上都有哪些跟计时/计数相关的模块,分别是什么特点,下面是详细列表。单从计时功能角度考虑,SysTick、GPT、PIT、TMR 都是不错的选择。
    12.png
    二、计时设计对coremark测试程序影响
    我们知道 coremark 标准的测试逻辑是在某配置参数组合下单位时间内跑了多少次 coremark 程序,一般情况下要求至少跑 10s 以上,因此计时部分的设计是很重要的。


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


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


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


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


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

    PIT 通道链接模式使能也很简单,主要在 PIT->CHANNEL[x].TCTRL[CHN] 位上,这个位开启后,channel x 就和 channel x-1 连了起来。下面是 channel 0 和 channel 1 串连组成 64bit 计数器的初始化代码:
    1. void timer_pit_init(void)
    2. {
    3.     // Turn on PIT: MDIS = 0, FRZ = 0
    4.     PIT->MCR = 0x00;

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

    11.     // Set up timer 0 to max value
    12.     PIT->CHANNEL[0].LDVAL = 0xFFFFFFFF;         // setup timer 0 for maximum counting period
    13.     PIT->CHANNEL[0].TFLG = 1;                   // clear the timer 0 flag
    14.     PIT->CHANNEL[0].TCTRL = PIT_TCTRL_TEN_MASK; // start timer 0
    15. }
    复制代码
    实际上我们也根本不需要 128bit 计数器,64bit 计数器就完全够用了,就以 150MHz 时钟源来说,超时一次需要约 3899 年,谁需要操心 3899 年后的事情呢?此外,在 channel 0 和 channel 1 串联的情况下,PIT 还提供了一个 64bit lifetime 计数器,直接读这个计数器就能获取当前 channel 0,1 串连的计数值(需要先读 LTMR64H[LTH],然后读 LTMR64L[LTL]),不用考虑手动读 channel 0,1 计数值可能会发生的潜在翻转问题(rollover)。
    14.png
    你看,使能了 PIT 通道链接用法后就完美地解决了 coremark 测试程序计时设计问题。
    1. uint64_t timer_pit_get_ticks() {
    2.     uint64_t valueH;
    3.     volatile uint32_t valueL;

    4. #if defined(FSL_FEATURE_PIT_HAS_LIFETIME_TIMER) && (FSL_FEATURE_PIT_HAS_LIFETIME_TIMER == 1)
    5.     valueH = PIT->LTMR64H;
    6.     valueL = PIT->LTMR64L;
    7. #else
    8.     do
    9.     {
    10.         valueL = PIT->CHANNEL[0].CVAL;
    11.         valueH = PIT->CHANNEL[1].CVAL;
    12.     } while (valueL < PIT->CHANNEL[0].CVAL);
    13. #endif // FSL_FEATURE_PIT_HAS_LIFETIME_TIMER

    14.     return ~((valueH << 32) | valueL);
    15. }
    复制代码
    四、关于coremark上计时的其他改进点
    最后再提两个 coremark 测试程序设计小改进点,一是在一些双核型号上(比如 i.MXRT1170, 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() 下不生效,所以做了个如下手动转换版:
    1. MAIN_RETURN_TYPE main(void) {
    2.     // 代码省略...
    3.     uint64_t total_time;

    4.     total_time=get_time();

    5.     //ee_printf("Total ticks      : %lu\n",(ee_u32)total_time);
    6.     if (total_time & (~(uint64_t)0xFFFFFFFF))
    7.     {
    8.         ee_printf("Total ticks      : ");
    9.         ee_printf("%lu",  (ee_u32)(total_time / 1000000000));
    10.         ee_printf("%lu\n",(ee_u32)(total_time % 1000000000));
    11.     }
    12.     else
    13.     {
    14.         ee_printf("Total ticks      : %lu\n",(ee_u32)total_time);
    15.     }

    16.     // 代码省略...
    17. }
    复制代码
    至此,i.MXRT定时器PIT的多通道链接模式及其在coremark测试里的应用痞子衡便介绍完毕了,掌声在哪里~~~









    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-4-10 22:38
  • 签到天数: 1335 天

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2022-3-25 15:52:55 | 显示全部楼层
    掌声,掌声

    生命周期计数,这个名称太形象了。
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    8 小时前
  • 签到天数: 1479 天

    [LV.10]以坛为家III

    203

    主题

    2万

    帖子

    64

    超级版主

    Rank: 8Rank: 8

    积分
    92624
    最后登录
    2024-4-26
    发表于 2022-3-26 22:07:03 | 显示全部楼层
    感谢分享·~~~
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-26 18:35 , Processed in 0.128031 second(s), 24 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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