GD32移植CoreMark实现性能测评 您所在的位置:网站首页 gd32f450移植到f407 GD32移植CoreMark实现性能测评

GD32移植CoreMark实现性能测评

2024-01-25 18:55| 来源: 网络整理| 查看: 265

简介

本文将演示CoreMark移植到GD32/STM32上进行性能测评。文章末尾给出了GD32F130C8T6、GD32F303CCT6、STM32F103ZET6、Air32F103CBT6这几款单片机的测试对比结果。

工程搭建

访问CoreMark的Github主页,下载如下图所示的源文件。在工程中新建一个子目录CoreMark,并放入其中。在Keil开发环境中,需要对CoreMark源文件(.c)加入到分组中,并指定CoreMark的头文件的包含路径,这些步骤不再赘述。

移植CoreMark

main函数

CoreMark代码框架定义了main函数,因此我们要屏蔽掉自己写的main函数。在这个mian函数中,首先调用了portable_init()函数,这个函数就是我们移植的时候,实现系统时钟以及串口初始化的地方。

int main(int argc, char *argv[]) { //省略 /* first call any initializations needed */ portable_init(&(results[0].port), &argc, argv); //省略 }

栈深度的调整

CoreMark默认使用栈内存实现数据的存储和计算,它会在main函数中定义一个2KB大小的数组。而在单片机的xxx_startup.s汇编启动文件中,对栈的容量限制为1KB,因此必须扩大栈容量,否则会进入HardFault。笔者因为这个问题调试了很久。建议定义为4KB,如下所示。

; Stack Configuration ; Stack Size (in Bytes) ; ;Stack_Size EQU 0x00000400 Stack_Size EQU 0x00001000

对串口的适配

CoreMark使用串口将测试信息输出,这样我们就可以在电脑的串口调试助手上看到评分。具体来说,CoreMark使用ee_printf()函数来输出数据。那么我们就要配置串口,并实现ee_printf()函数。

在portable_init()函数中我们实现对串口的配置,并实现ee_printf()函数,如下:

//#include "xxxxx" //MCU指定的头文件 #include #include #include //USART0配置函数 void USART0_config(void) { rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_USART0); //PA9 USART0_Tx gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); //PA10 USART0_Rx gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_10); usart_deinit(USART0); usart_baudrate_set(USART0, 115200); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); } //实现ee_printf int ee_printf(const char *fmt, ...) { char buf[256]; va_list args; va_start(args, fmt); int len = vsnprintf((char*)buf,256,fmt,args); //需要C99支持 va_end(args); if(len > 0) { for(int i=0;iportable_id = 1; }

由于我们直接实现了ee_printf()函数而不是依赖C标准库中的printf,因此需要在core_portme.h中对将HAS_PRINTF 宏修改为0,并同时声明ee_printf()函数。

#ifndef HAS_PRINTF #define HAS_PRINTF 0 int ee_printf(const char *fmt, ...) ; #endif

对SysTick的适配

CoreMark需要使用一个定时器进行计时,它的主要操作流程为:开启定时器,运行性能测试代码片段,停止定时器,计算时间差值,用这个差值来衡量CPU的性能,差值越小说明CPU的计算速度越快。由于SysTick是对于CM3/CM4内核的单片机来说是标配,且配置过程简单,所以本文使用SysTick作为CoreMark的时基。

对SysTick的配置在core_portme.c文件中进行。在这个文件中,CoreMark定义了2个全局变量start_time_val和stop_time_val。每次迭代开始的时候,CoreMark使用start_time()函数来初始化并启动SysTick,并使用start_time_val保存启动SysTick时的初始tick数,由于tick初始值为0,所以start_time_val初始化为0即可。

我们需要自己在这个文件中定义一个全局变量Tick,来记录从定时器启动以来经过的tick数。在本文中,笔者在start_time()函数使用CMSIS定义的SysTick_Config()函数来配置SysTick每1ms中断一次,并在中断函数中递增全局变量Tick。

每次迭代完成后,CoreMark使用stop_time()函数来停止SysTick,并将Tick数存储在全局变量start_time_val中。然后,CoreMark使用get_time()函数来计算得到从定时器启动到结束经过的tick数,然后通过time_in_secs()函数将tick数转换为秒数。为了能让CoreMark正确计算出秒数,我们必须根据自己的定时器配置正确定义EE_TICKS_PER_SEC  的值,这个宏代表了安装我们当前的定时器配置,1秒会产生多少个tick。本文SysTick配置为1ms中断一次,因此定义为1000。CoreMark预定义的get_time()函数和time_in_secs()函数可以不用修改。

//注释原有的EE_TICKS_PER_SEC 的定义,重新定义为1000,也就是每秒钟SysTick中断1000次,产生1000个tick计数 //#define EE_TICKS_PER_SEC (NSECS_PER_SEC / TIMER_RES_DIVIDER) #define EE_TICKS_PER_SEC 1000 //每隔1ms中断一次,也就是1秒有1000个tick volatile uint32_t Tick ; //定义SysTick的中断次数计数器,每中断一次,就在中断函数中加1 //SysTick的中断函数 void SysTick_Handler(void) { Tick++; } //启动定时器并将start_time_val设置为当前的定时器Tick数 void start_time(void) { //GETMYTIME(&start_time_val); Tick = 0; //每次初始化时让tick数从0开始 start_time_val = Tick; //当前tick数保存到全局变量start_time_val SysTick_Config(SystemCoreClock/1000); //配置并启动SysTick,这里配置Systick每隔1ms中断一次 } //停止定时器并将stop_time_val设置为当前的定时器Tick数 void stop_time(void) { //GETMYTIME(&stop_time_val); SysTick->CTRL = 0; //停止SysTick stop_time_val = Tick; //当前tick数保存到全局变量stop_time_val } //==========================【以下代码不用修改】============================== //通过stop_time_val和start_time_val的差值得到CPU跑评测代码消耗的时间,单位是tick数。 CORE_TICKS get_time(void) { CORE_TICKS elapsed = (CORE_TICKS) (MYTIMEDIFF(stop_time_val, start_time_val)); return elapsed; } //将get_time()函数返回的tick数转换为秒单位时间。 //需要根据定时器的tick周期正确定义EE_TICKS_PER_SEC的值。 secs_ret time_in_secs(CORE_TICKS ticks) { secs_ret retval = ((secs_ret)ticks) / (secs_ret)EE_TICKS_PER_SEC; return retval; }

如果你想超频

像STM32,GD32这种都是在Reset_Handler函数中调用SystemInit() 函数 执行的系统时钟初始化,也就是在复位完成后系统时钟就配置好了。如果你想超频,可以在portable_init()函数中对系统时钟进行二次配置,实现超频。

其他配置

在core_portme.h中对ITERATIONS进行定义,它定义了测试代码迭代运行的次数,这个值的大小不会影响评分结果,但是必须要让单片机运行至少10秒,值太大会让评测时间变长。这里给个参考值,一般72MHz的主频可以使用2000,120MHz可以使用3500。

//####################################### #define ITERATIONS 3500 /*定义迭代次数*/ //#######################################

在core_portme.h中对MAIN_HAS_NOARGC进行定义,定义为1时,coremark使用的main函数的参数列表为空;定义为0时coremark使用的main函数的参数列表为main(int argc, char *argv[])。由于单片机的main函数一般都是不带参数的,因此这里修改为1。

#ifndef MAIN_HAS_NOARGC #define MAIN_HAS_NOARGC 1 #endif

在core_portme.h中对编译器版本和编译器参数进行定义。这2个宏不会影响跑分结果,只是coremark在打印评测结果的时候会输出定义的值。如果你只想看单片机性能,这2个宏可以不用在意。

#define COMPILER_VERSION "Armcc v5.06" #define COMPILER_FLAGS \ "-O3 -Otime"

实验结果

输出样例

实验环境

CoreMark 1.0 , run on 2KB Stack MemoryKeil MDK V536 with ArmCC V5.06 Update 7 MCU内核CPU主频编译器和编译参数每秒迭代次数*功耗GD32F303CCT6M4120MArmcc v5.06 -O3 -Otime279.3073183.3V@17mAGD32F303CCT6M472MArmcc v5.06 -O3 -Otime167.5603223.3V@11mAGD32F130C8T6M372MArmcc v5.06 -O3 -Otime162.7377133.3V@23mAGD32F130C8T6M348MArmcc v5.06 -O3 -Otime108.4699543.3V@17mASTM32F103ZET6M372MArmcc v5.06 -O3 -Otime134.188162 Air32F103CBT6M372MArmcc v5.06 -O3 -Otime184.162063 3.3V@18mAAir32F103CBT6M3120MArmcc v5.06 -O3 -Otime306.983883 3.3V@23mA

注*:数值越大代表CPU运算能力越强



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有