CoreMark是用来衡量嵌入式系统中中心处理单元(CPU,或叫做微控制器MCU)性能的标准。该标准于2009年由EEMBC组织的Shay Gla-On提出,并且试图将其发展成为工业标准,从而代替陈旧的Dhrystone标准。代码使用C语言写成,包含如下的运算法则:列举(寻找并排序),数学矩阵操作(普通矩阵运算)和状态机(用来确定输入流中是否包含有效数字),最后还包括CRC(循环冗余校验)。用户可以自行下载CoreMark程序,移植到测试平台上,随后运行就可以看到分数了。——《百度百科》
2.CoreMark移植
2.1.下载CoreMark程序
CoreMark的官网为https://www.eembc.org/coremark/index.php ,点击官网上的Download会提示转到GitHub上去获取代码。我们下载最新版本的软件代码压缩包。
打开coremark-master压缩包,我们可以看到CoreMark使用到的源程序,如下图所示。需要我们做修改的是simple文件夹中的core_portme.c和core_portme.h两个接口程序。
2.2.CoreMark程序移植
首先需要配置STM32L412KB的系统运行时钟,让芯片运行在允许范围内的最大运行频率;其次需要配置串口输出打印消息。这两个操作可以参照我上一个帖子NUCLEO-L412KB 开箱评测
接着我们将CoreMark的程序添加到我们的KEIL工程中,分为CoreMark和CoreMark_Portable两个分组,如下图所示:
修改core_portme.c的代码:将系统的初始化操作添加到portable_init函数;修改start_time和stop_time函数,这两个函数是在进行运算能力测试前后调用的,用来统计程序运行时长的;因为我们STM32L412KB设置的SysTick中断时间是1ms,所以我们需要将宏NSECS_PER_SEC的值修改为1000;在系统SysTick_Handler中断函数中添加CoreMark的时间计数。具体的代码如下所示:
/** * Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Original Author: Shay Gal-on */ /* Includes ------------------------------------------------------------------*/ #include "platform.h" #include "coremark.h" /* Private defines ----------------------------------------------------------*/ #define ITERATIONS 2500 /* Private variables --------------------------------------------------------*/ volatile uint8_t stop_time_flg = 1; /* Exported function prototypes ---------------------------------------------*/ extern void InitSystem(void); #if VALIDATION_RUN volatile ee_s32 seed1_volatile = 0x3415; volatile ee_s32 seed2_volatile = 0x3415; volatile ee_s32 seed3_volatile = 0x66; #endif #if PERFORMANCE_RUN volatile ee_s32 seed1_volatile = 0x0; volatile ee_s32 seed2_volatile = 0x0; volatile ee_s32 seed3_volatile = 0x66; #endif #if PROFILE_RUN volatile ee_s32 seed1_volatile = 0x8; volatile ee_s32 seed2_volatile = 0x8; volatile ee_s32 seed3_volatile = 0x8; #endif volatile ee_s32 seed4_volatile = ITERATIONS; volatile ee_s32 seed5_volatile = 0x0; /** * Porting : Timing functions * How to capture time and convert to seconds must be ported to whatever is supported by the platform. * e.g. Read value from on board RTC, read value from cpu clock cycles performance counter etc. * Sample implementation for standard time.h and windows.h definitions included. */ /** * Define : TIMER_RES_DIVIDER * Divider to trade off timer resolution and total time that can be measured. * * Use lower values to increase resolution, but make sure that overflow does not occur. * If there are issues with the return value overflowing, increase this value. */ #define NSECS_PER_SEC 1000 #define CORETIMETYPE clock_t #define MYTIMEDIFF(fin,ini) ((fin)-(ini)) #define TIMER_RES_DIVIDER 1 #define EE_TICKS_PER_SEC (NSECS_PER_SEC / TIMER_RES_DIVIDER) /** * Define Host specific (POSIX), or target specific global time variables. */ static CORETIMETYPE start_time_val = 0, stop_time_val = 0; /** * Function : start_time * This function will be called right before starting the timed portion of the benchmark. * * Implementation may be capturing a system timer (as implemented in the example code) * or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0. */ void start_time(void) { stop_time_flg = 0; stop_time_val = 0; } /** * Function : stop_time * This function will be called right after ending the timed portion of the benchmark. * * Implementation may be capturing a system timer (as implemented in the example code) * or other system parameters - e.g. reading the current value of cpu cycles counter. */ void stop_time(void) { stop_time_flg = 1; } /** * Function : get_time * Return an abstract "ticks" number that signifies time on the system. * * Actual value returned may be cpu cycles, milliseconds or any other value, * as long as it can be converted to seconds by <time_in_secs>. * This methodology is taken to accomodate any hardware or simulated platform. * The sample implementation returns millisecs by default, * and the resolution is controlled by <TIMER_RES_DIVIDER> */ CORE_TICKS get_time(void) { CORE_TICKS elapsed=(CORE_TICKS)(MYTIMEDIFF(stop_time_val, start_time_val)); return elapsed; } /** * Function : time_in_secs * Convert the value returned by get_time to seconds. * * The <secs_ret> type is used to accomodate systems with no support for floating point. * Default implementation implemented by the EE_TICKS_PER_SEC macro above. */ secs_ret time_in_secs(CORE_TICKS ticks) { secs_ret retval=((secs_ret)ticks) / (secs_ret)EE_TICKS_PER_SEC; return retval; } ee_u32 default_num_contexts = 1; /** * Function : portable_init * Target specific initialization code * Test for some common mistakes. */ void portable_init(core_portable *p, int *argc, char *argv[]) { InitSystem(); if (sizeof(ee_ptr_int) != sizeof(ee_u8 *)) { ee_printf("ERROR! Please define ee_ptr_int to a type that holds a pointer!\n"); } if (sizeof(ee_u32) != 4) { ee_printf("ERROR! Please define ee_u32 to a 32b unsigned type!\n"); } p->portable_id=1; } /** * Function : portable_fini * Target specific final code */ void portable_fini(core_portable *p) { p->portable_id=0; } /** * @brief : time.h * @param : * @returns: * @details: */ void CoreMark_IncTick(void) { if(stop_time_flg == 0) stop_time_val++; } /************************ (C) COPYRIGHT ************************END OF FILE****/
复制代码在运行CoreMark程序时候,它有自己的main入口函数,和我们CubeMX产生的main函数就冲突了,所以在这个代码中,我们将CubeMX生成的main函数暂时修改为user_main函数。
在core_portme.h文件中对MEM_METHOD及其它的一些宏定义进行修改。CoreMark程序默认的MEM_METHOD是MEM_STACK,通过看代码发现用于测试的一个2KB大小的数据是存放在STACK中的,但是CubeMX自动生成的Stack_Size为0x400;所以在不修改MEM_METHOD和Stack_Size的情况下,程序运行肯定会进入硬件错误中断的,这也是个坑呀,大家注意!!!我们现在将MEM_METHOD修改为MEM_STATIC,即2KB的测试数据是以静态变量的形式存放在RAM中,这们程序运行就没有问题啦^^。具体的core_portme.h代码如下所示:
/** * Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Original Author: Shay Gal-on */ /** * Topic : Description * This file contains configuration constants required to execute on different platforms */ #ifndef CORE_PORTME_H #define CORE_PORTME_H /***************************/ /* Data types and settings */ /***************************/ /** * Configuration : HAS_FLOAT * Define to 1 if the platform supports floating point. */ #ifndef HAS_FLOAT #define HAS_FLOAT 1 #endif /** * Configuration : HAS_TIME_H * Define to 1 if platform has the time.h header file, * and implementation of functions thereof. */ #ifndef HAS_TIME_H #define HAS_TIME_H 1 #endif /** * Configuration : USE_CLOCK * Define to 1 if platform has the time.h header file, * and implementation of functions thereof. */ #ifndef USE_CLOCK #define USE_CLOCK 1 #endif /** * Configuration : HAS_STDIO * Define to 1 if the platform has stdio.h. */ #ifndef HAS_STDIO #define HAS_STDIO 1 #endif /** * Configuration : HAS_PRINTF * Define to 1 if the platform has stdio.h and implements the printf function. */ #ifndef HAS_PRINTF #define HAS_PRINTF 1 #endif /** * Configuration : CORE_TICKS * Define type of return from the timing functions. */ #include <time.h> typedef clock_t CORE_TICKS; /** * Definitions : COMPILER_VERSION, COMPILER_FLAGS, MEM_LOCATION * Initialize these strings per platform */ #ifndef COMPILER_VERSION #define COMPILER_VERSION "Please put compiler version here (e.g. gcc 4.1)" #endif #ifndef COMPILER_FLAGS #define COMPILER_FLAGS "-g -O3" #endif #ifndef MEM_LOCATION #define MEM_LOCATION "STATIC" #endif /** * Data Types : * To avoid compiler issues, define the data types that need ot be used for 8b, 16b and 32b in <core_portme.h>. * * *Imprtant* : * ee_ptr_int needs to be the data type used to hold pointers, otherwise coremark may fail!!! */ typedef signed short ee_s16; typedef unsigned short ee_u16; typedef signed int ee_s32; typedef double ee_f32; typedef unsigned char ee_u8; typedef unsigned int ee_u32; typedef ee_u32 ee_ptr_int; typedef size_t ee_size_t; /** * align_mem : * This macro is used to align an offset to point to a 32b value. It is used in the Matrix algorithm to initialize the input memory blocks. */ #define align_mem(x) (void *)(4 + (((ee_ptr_int)(x) - 1) & ~3)) /** * Configuration : SEED_METHOD * Defines method to get seed values that cannot be computed at compile time. * * Valid values : * SEED_ARG - from command line. * SEED_FUNC - from a system function. * SEED_VOLATILE - from volatile variables. */ #ifndef SEED_METHOD #define SEED_METHOD SEED_VOLATILE #endif /** * Configuration : MEM_METHOD * Defines method to get a block of memry. * * Valid values : * MEM_MALLOC - for platforms that implement malloc and have malloc.h. * MEM_STATIC - to use a static memory array. * MEM_STACK - to allocate the data block on the stack (NYI). */ #ifndef MEM_METHOD #define MEM_METHOD MEM_STATIC #endif /** * Configuration : MULTITHREAD * Define for parallel execution * * Valid values : * 1 - only one context (default). * N>1 - will execute N copies in parallel. * * Note : * If this flag is defined to more then 1, an implementation for launching parallel contexts must be defined. * * Two sample implementations are provided. Use <USE_PTHREAD> or <USE_FORK> to enable them. * * It is valid to have a different implementation of <core_start_parallel> and <core_end_parallel> in <core_portme.c>, * to fit a particular architecture. */ #ifndef MULTITHREAD #define MULTITHREAD 1 #define USE_PTHREAD 0 #define USE_FORK 0 #define USE_SOCKET 0 #endif /** * Configuration : MAIN_HAS_NOARGC * Needed if platform does not support getting arguments to main. * * Valid values : * 0 - argc/argv to main is supported * 1 - argc/argv to main is not supported * * Note : * This flag only matters if MULTITHREAD has been defined to a value greater then 1. */ #ifndef MAIN_HAS_NOARGC #define MAIN_HAS_NOARGC 0 #endif /** * Configuration : MAIN_HAS_NORETURN * Needed if platform does not support returning a value from main. * * Valid values : * 0 - main returns an int, and return value will be 0. * 1 - platform does not support returning a value from main */ #ifndef MAIN_HAS_NORETURN #define MAIN_HAS_NORETURN 0 #endif /** * Variable : default_num_contexts * Not used for this simple port, must cintain the value 1. */ extern ee_u32 default_num_contexts; typedef struct CORE_PORTABLE_S { ee_u8 portable_id; } core_portable; /* Exported functions prototypes ---------------------------------------------*/ void portable_init(core_portable *p, int *argc, char *argv[]); void portable_fini(core_portable *p); #if !defined(PROFILE_RUN) && !defined(PERFORMANCE_RUN) && !defined(VALIDATION_RUN) #if (TOTAL_DATA_SIZE == 1200) #define PROFILE_RUN 1 #elif (TOTAL_DATA_SIZE == 2000) #define PERFORMANCE_RUN 1 #else #define VALIDATION_RUN 1 #endif #endif #endif /************************ (C) COPYRIGHT ************************END OF FILE****/
复制代码3.CoreMark运行测试
3.1.在运行之前我们还需要对ITERATIONS这个宏的值进行修改,这个数值的大小直接影响到程序运行时长;通过代码ee_printf("ERROR! Must execute for at least 10 secs for a valid result!\r\n");我们得知,程序运算能力的测试最小要运行10秒以上的时间,我们在测试STM32L412KB时将ITERATIONS的值修改为2500。
3.2.编译程序无误后,将程序下载到NUCLEO-L412KB开发板运行,在等待了将近10多秒后,有了输出结果,如下图所示:
4.工程源代码