1 perf_counter 简介
为Cortex-M Systick提供专用的性能计数器。它与用户原始SysTick功能共享SysTick,而不会干扰它们。该库将带来新的功能,例如性能计数器、delay_us和clock()服务定义在time.h中。
仓库地址如下: https://github.com/GorgonMeducer/perf_counter
2 移植 perf_counter
2.1 新建工程
以之前的工程 at32l021_blinky 工程为模板,复制一份,重名为 at32l021_coremark。
2.1.1 添加 Debug 配置
删除原来的 Debug 配置,重新添加一个 at32l021_coremark 配置,如下所示,切记要勾选 WinUSB
2.2 添加 perf_counter 到工程
在工程目录新建一个 3rd_party 目录,并拷贝 perf_counter 源码到 3rd_party 目录下,然后在 AT32IDE 中刷新一下工程,就可以看到新加入的 perf_counter 源码。
2.3 排除无需使用的文件
perf_counter 目录下的 example 文件夹下的所有文件显然不需要加入编译,右键点击此文件夹,然后再弹出的菜单中选择 Properties ,修改其属性。
选择 C/C++ Build 然后勾选 Exclude resource from Build。
注意,考虑到 Debug/Release 两个配置都需要排除这个文件夹,所以也需要选择配置为 All configurations。
最终排除了如下文件和文件夹:
- 3rd_party/perf_counter/benchmark/coremark/barebones
- 3rd_party/perf_counter/benchmark/coremark/freebsd
- 3rd_party/perf_counter/benchmark/coremark/linux
- 3rd_party/perf_counter/benchmark/coremark/macos
- 3rd_party/perf_counter/benchmark/coremark/posix
- 3rd_party/perf_counter/benchmark/coremark/rtems
- 3rd_party/perf_counter/benchmark/coremark/simple
- 3rd_party/perf_counter/benchmark/coremark/zephyr
- 3rd_party/perf_counter/benchmark/coremark/core_main.c
- 3rd_party/perf_counter/example
- 3rd_party/perf_counter/os
- 3rd_party/perf_counter/template
- 3rd_party/perf_counter/systick_wrapper_gnu.s
- 3rd_party/perf_counter/systick_wrapper_ual.s
2.4 编译报错 coremark.h 文件找不到
在排除了不需要的文件之后,编译工程,提示 coremark.h 文件找不到。
显示这是头文件路径没有找到,按如下方式添加 perf_counter 需要的头文件路径:
切记,这里修改 All configurations 配置,一下子实现 Debug/Release 两个配置,都增加了如下的3个头文件搜索路径。
2.5 编译报错,__cycleof__() 需要开启 GNU 扩展
按照如下步骤,修改编译器语言标准为 gnu99
2.6 coremark_main() 函数报错
这个函数有几个版本,最终选用perf_counter.h 定义的版本,它的原型如下,返回值和入口参数都是 void 类型。但是其他的版本入口参数不是 void 类型。
解决办法如下,新增两个宏定义,分别是 MAIN_HAS_NOARG=1 和 MAIN_HAS_NORETURN。配置方法如下
2.7 汇编文件 systick_wrapper_gnu.s 报错
当前是GCC编译器,排除两个汇编文件,分别是
systick_wrapper_gnu.s
systick_wrapper_ual.s
2.8 delay_us() 和 delay_ms() 两个函数重复定义
因为 perf_counter 已经提供了这两个函数,但是 at32l021_board.c 也提供了这两个函数的实现,所以重复了。
这里推荐使用 perf_counter 的延时函数版本,因为更通用、更精准。
解决办法是屏蔽 at32l021_board.c 中这两个函数的实现,用 #if 0 括住这两个函数即可。
2.9 编译报错 SystemCoreClork 变量未定义
perf_counter 计时需要使用这个全局变量,它需要精准的表示CPU时钟频率。
显然当前 AT32L021 BSP没有定义这个变量,而是在 cmsis/system_at32l021.c 文件中定义了一个 system_core_clock 变量,它的作用就是 SystemCoreClock 。奈何 AT32 标新立异,取了一个和 CMSIS 标准不同的变量名。
解决办法是新增一个变量 SystemCoreClock,并且及时更新此变量的值。
在函数 system_core_clock_update() 尾部更新 SystemCoreClock 变量的值。
此外编译还是报错,因为 AT32 BSP又有如下的奇葩宏定义,注释这个宏定义即可。
2.10 编译通过
3 解决 perf_counter 调用时的编译报错
在 main.c 头部引入 perf_counter.h 头文件,然后加入下面的代码,初始化 perf_counter,并测量 LED2 翻转一次耗费了多少个时钟周期。
这里 init_cycle_counter(false) 表示让 perf_counter 接管 SysTick ,那么 at32_board_init() 函数中就不需要初始 SysTick 了,修改如下:
同时把 at32l021_board.c 中三个 `delay_xxx()` 函数都屏蔽,同时 at32l021_board.h 中的函数声明也屏蔽。
最后还需要在 SysTick_Handler() 中增加一行函数调用,如下图所示:
4 测试 LED4 翻转速度
最后修改测试代码,在 main_loop() 中使用如下代码测试 LED4 的翻转占用的时钟周期数。
运行结果如下,可知 LED4 翻转一次占用的时钟周期数大约是 62~72 个时钟周期。
4.1 Release 版本运行对比
把工程 Debug 配置复制一份,重命名为 Release,如下图所示:
然后修改工程 Release 版本编译选项,优化等级修改为 -Ofast
Release 版本的程序运行结果
可以看到 Release 版本的 LED4 翻转速度在13个时钟周期,而 Debug 版本的时钟周期是62个,速度快了约5倍。
|