1.概述

    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上去获取代码。我们下载最新版本的软件代码压缩包。
    1.png

    打开coremark-master压缩包,我们可以看到CoreMark使用到的源程序,如下图所示。需要我们做修改的是simple文件夹中的core_portme.c和core_portme.h两个接口程序。
    2.png

    2.2.CoreMark程序移植
    首先需要配置STM32L412KB的系统运行时钟,让芯片运行在允许范围内的最大运行频率;其次需要配置串口输出打印消息。这两个操作可以参照我上一个帖子NUCLEO-L412KB 开箱评测
    接着我们将CoreMark的程序添加到我们的KEIL工程中,分为CoreMark和CoreMark_Portable两个分组,如下图所示:
    3.png

    修改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****/
  • 复制代码
        2.3.CoreMark运行配置
        在运行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****/
  • 复制代码
        此外还需要对KEIL的配置做一下优化,具体如下图所示:
        5.png
        6.png

    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.png

    4.工程源代码

    coremark-master.zip (485.04 KB, 下载次数: 1)