xld0932

  • 7 主题
  • 11 帖子
  • 472 积分
  • 身份:LV2 初级技术员
  • E币:585

NUCLEO-L412KB使用CoreMark进行运算能力测试

2019-5-13 09:24:52 显示全部楼层
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的时间计数。具体的代码如下所示:
  1. /**
  2. * Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *     http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Original Author: Shay Gal-on
  17. */


  18. /* Includes ------------------------------------------------------------------*/
  19. #include "platform.h"
  20. #include "coremark.h"


  21. /* Private defines ----------------------------------------------------------*/
  22. #define ITERATIONS  2500


  23. /* Private variables --------------------------------------------------------*/
  24. volatile uint8_t stop_time_flg = 1;


  25. /* Exported function prototypes ---------------------------------------------*/
  26. extern  void InitSystem(void);


  27. #if VALIDATION_RUN
  28. volatile ee_s32 seed1_volatile = 0x3415;
  29. volatile ee_s32 seed2_volatile = 0x3415;
  30. volatile ee_s32 seed3_volatile = 0x66;
  31. #endif

  32. #if PERFORMANCE_RUN
  33. volatile ee_s32 seed1_volatile = 0x0;
  34. volatile ee_s32 seed2_volatile = 0x0;
  35. volatile ee_s32 seed3_volatile = 0x66;
  36. #endif

  37. #if PROFILE_RUN
  38. volatile ee_s32 seed1_volatile = 0x8;
  39. volatile ee_s32 seed2_volatile = 0x8;
  40. volatile ee_s32 seed3_volatile = 0x8;
  41. #endif

  42. volatile ee_s32 seed4_volatile = ITERATIONS;
  43. volatile ee_s32 seed5_volatile = 0x0;


  44. /**
  45. * Porting : Timing functions
  46. * How to capture time and convert to seconds must be ported to whatever is supported by the platform.
  47. * e.g. Read value from on board RTC, read value from cpu clock cycles performance counter etc.
  48. * Sample implementation for standard time.h and windows.h definitions included.
  49. */


  50. /**
  51. * Define : TIMER_RES_DIVIDER
  52. * Divider to trade off timer resolution and total time that can be measured.
  53. *
  54. * Use lower values to increase resolution, but make sure that overflow does not occur.
  55. * If there are issues with the return value overflowing, increase this value.
  56. */
  57. #define NSECS_PER_SEC                   1000
  58. #define CORETIMETYPE                    clock_t
  59. #define MYTIMEDIFF(fin,ini)             ((fin)-(ini))
  60. #define TIMER_RES_DIVIDER               1
  61. #define EE_TICKS_PER_SEC                (NSECS_PER_SEC / TIMER_RES_DIVIDER)


  62. /**
  63. * Define Host specific (POSIX), or target specific global time variables.
  64. */
  65. static CORETIMETYPE start_time_val = 0, stop_time_val = 0;


  66. /**
  67. * Function : start_time
  68. * This function will be called right before starting the timed portion of the benchmark.
  69. *
  70. * Implementation may be capturing a system timer (as implemented in the example code)
  71. * or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
  72. */
  73. void start_time(void)
  74. {
  75.     stop_time_flg = 0;
  76.     stop_time_val = 0;
  77. }


  78. /**
  79. * Function : stop_time
  80. * This function will be called right after ending the timed portion of the benchmark.
  81. *
  82. * Implementation may be capturing a system timer (as implemented in the example code)
  83. * or other system parameters - e.g. reading the current value of cpu cycles counter.
  84. */
  85. void stop_time(void)
  86. {
  87.         stop_time_flg = 1;
  88. }


  89. /**
  90. * Function : get_time
  91. * Return an abstract "ticks" number that signifies time on the system.
  92. *
  93. * Actual value returned may be cpu cycles, milliseconds or any other value,
  94. * as long as it can be converted to seconds by <time_in_secs>.
  95. * This methodology is taken to accomodate any hardware or simulated platform.
  96. * The sample implementation returns millisecs by default,
  97. * and the resolution is controlled by <TIMER_RES_DIVIDER>
  98. */
  99. CORE_TICKS get_time(void)
  100. {
  101.         CORE_TICKS elapsed=(CORE_TICKS)(MYTIMEDIFF(stop_time_val, start_time_val));
  102.         return elapsed;
  103. }


  104. /**
  105. * Function : time_in_secs
  106. * Convert the value returned by get_time to seconds.
  107. *
  108. * The <secs_ret> type is used to accomodate systems with no support for floating point.
  109. * Default implementation implemented by the EE_TICKS_PER_SEC macro above.
  110. */
  111. secs_ret time_in_secs(CORE_TICKS ticks)
  112. {
  113.     secs_ret retval=((secs_ret)ticks) / (secs_ret)EE_TICKS_PER_SEC;
  114.     return retval;
  115. }


  116. ee_u32 default_num_contexts = 1;


  117. /**
  118. * Function : portable_init
  119. * Target specific initialization code
  120. * Test for some common mistakes.
  121. */
  122. void portable_init(core_portable *p, int *argc, char *argv[])
  123. {
  124.     InitSystem();

  125.     if (sizeof(ee_ptr_int) != sizeof(ee_u8 *))
  126.     {
  127.         ee_printf("ERROR! Please define ee_ptr_int to a type that holds a pointer!\n");
  128.     }

  129.     if (sizeof(ee_u32) != 4)
  130.     {
  131.         ee_printf("ERROR! Please define ee_u32 to a 32b unsigned type!\n");
  132.     }

  133.     p->portable_id=1;
  134. }


  135. /**
  136. * Function : portable_fini
  137. * Target specific final code
  138. */
  139. void portable_fini(core_portable *p)
  140. {
  141.     p->portable_id=0;
  142. }


  143. /**
  144. * @brief  : time.h
  145. * @param  :
  146. * @returns:
  147. * @details:
  148. */
  149. void CoreMark_IncTick(void)
  150. {
  151.     if(stop_time_flg == 0) stop_time_val++;
  152. }


  153. /************************ (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代码如下所示:
  1. /**
  2. * Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0

  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. *
  15. * Original Author: Shay Gal-on
  16. */

  17. /**
  18. * Topic : Description
  19. * This file contains configuration constants required to execute on different platforms
  20. */
  21. #ifndef CORE_PORTME_H
  22. #define CORE_PORTME_H


  23. /***************************/
  24. /* Data types and settings */
  25. /***************************/


  26. /**
  27. * Configuration : HAS_FLOAT
  28. * Define to 1 if the platform supports floating point.
  29. */
  30. #ifndef HAS_FLOAT
  31. #define HAS_FLOAT   1
  32. #endif


  33. /**
  34. * Configuration : HAS_TIME_H
  35. * Define to 1 if platform has the time.h header file,
  36. * and implementation of functions thereof.
  37. */
  38. #ifndef HAS_TIME_H
  39. #define HAS_TIME_H  1
  40. #endif


  41. /**
  42. * Configuration : USE_CLOCK
  43. * Define to 1 if platform has the time.h header file,
  44. * and implementation of functions thereof.
  45. */
  46. #ifndef USE_CLOCK
  47. #define USE_CLOCK   1
  48. #endif


  49. /**
  50. * Configuration : HAS_STDIO
  51. * Define to 1 if the platform has stdio.h.
  52. */
  53. #ifndef HAS_STDIO
  54. #define HAS_STDIO   1
  55. #endif


  56. /**
  57. * Configuration : HAS_PRINTF
  58. * Define to 1 if the platform has stdio.h and implements the printf function.
  59. */
  60. #ifndef HAS_PRINTF
  61. #define HAS_PRINTF  1
  62. #endif


  63. /**
  64. * Configuration : CORE_TICKS
  65. * Define type of return from the timing functions.
  66. */
  67. #include <time.h>
  68. typedef clock_t CORE_TICKS;


  69. /**
  70. * Definitions : COMPILER_VERSION, COMPILER_FLAGS, MEM_LOCATION
  71. * Initialize these strings per platform
  72. */
  73. #ifndef COMPILER_VERSION
  74. #define COMPILER_VERSION    "Please put compiler version here (e.g. gcc 4.1)"
  75. #endif

  76. #ifndef COMPILER_FLAGS
  77. #define COMPILER_FLAGS      "-g -O3"
  78. #endif


  79. #ifndef MEM_LOCATION
  80. #define MEM_LOCATION        "STATIC"
  81. #endif


  82. /**
  83. * Data Types :
  84. * To avoid compiler issues, define the data types that need ot be used for 8b, 16b and 32b in <core_portme.h>.
  85. *
  86. * *Imprtant* :
  87. * ee_ptr_int needs to be the data type used to hold pointers, otherwise coremark may fail!!!
  88. */
  89. typedef signed short    ee_s16;
  90. typedef unsigned short  ee_u16;
  91. typedef signed int      ee_s32;
  92. typedef double          ee_f32;
  93. typedef unsigned char   ee_u8;
  94. typedef unsigned int    ee_u32;
  95. typedef ee_u32          ee_ptr_int;
  96. typedef size_t          ee_size_t;


  97. /**
  98. * align_mem :
  99. * 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.
  100. */
  101. #define align_mem(x) (void *)(4 + (((ee_ptr_int)(x) - 1) & ~3))


  102. /**
  103. * Configuration : SEED_METHOD
  104. * Defines method to get seed values that cannot be computed at compile time.
  105. *
  106. * Valid values :
  107. * SEED_ARG - from command line.
  108. * SEED_FUNC - from a system function.
  109. * SEED_VOLATILE - from volatile variables.
  110. */
  111. #ifndef SEED_METHOD
  112. #define SEED_METHOD     SEED_VOLATILE
  113. #endif


  114. /**
  115. * Configuration : MEM_METHOD
  116. * Defines method to get a block of memry.
  117. *
  118. * Valid values :
  119. * MEM_MALLOC - for platforms that implement malloc and have malloc.h.
  120. * MEM_STATIC - to use a static memory array.
  121. * MEM_STACK - to allocate the data block on the stack (NYI).
  122. */
  123. #ifndef MEM_METHOD
  124. #define MEM_METHOD      MEM_STATIC
  125. #endif


  126. /**
  127. * Configuration : MULTITHREAD
  128. * Define for parallel execution
  129. *
  130. * Valid values :
  131. * 1 - only one context (default).
  132. * N>1 - will execute N copies in parallel.
  133. *
  134. * Note :
  135. * If this flag is defined to more then 1, an implementation for launching parallel contexts must be defined.
  136. *
  137. * Two sample implementations are provided. Use <USE_PTHREAD> or <USE_FORK> to enable them.
  138. *
  139. * It is valid to have a different implementation of <core_start_parallel> and <core_end_parallel> in <core_portme.c>,
  140. * to fit a particular architecture.
  141. */
  142. #ifndef MULTITHREAD
  143. #define MULTITHREAD     1
  144. #define USE_PTHREAD     0
  145. #define USE_FORK        0
  146. #define USE_SOCKET      0
  147. #endif


  148. /**
  149. * Configuration : MAIN_HAS_NOARGC
  150. * Needed if platform does not support getting arguments to main.
  151. *
  152. * Valid values :
  153. * 0 - argc/argv to main is supported
  154. * 1 - argc/argv to main is not supported
  155. *
  156. * Note :
  157. * This flag only matters if MULTITHREAD has been defined to a value greater then 1.
  158. */
  159. #ifndef MAIN_HAS_NOARGC
  160. #define MAIN_HAS_NOARGC     0
  161. #endif


  162. /**
  163. * Configuration : MAIN_HAS_NORETURN
  164. * Needed if platform does not support returning a value from main.
  165. *
  166. * Valid values :
  167. * 0 - main returns an int, and return value will be 0.
  168. * 1 - platform does not support returning a value from main
  169. */
  170. #ifndef MAIN_HAS_NORETURN
  171. #define MAIN_HAS_NORETURN   0
  172. #endif


  173. /**
  174. * Variable : default_num_contexts
  175. * Not used for this simple port, must cintain the value 1.
  176. */
  177. extern ee_u32 default_num_contexts;

  178. typedef struct CORE_PORTABLE_S
  179. {
  180.     ee_u8 portable_id;
  181. } core_portable;


  182. /* Exported functions prototypes ---------------------------------------------*/
  183. void portable_init(core_portable *p, int *argc, char *argv[]);
  184. void portable_fini(core_portable *p);


  185. #if !defined(PROFILE_RUN) && !defined(PERFORMANCE_RUN) && !defined(VALIDATION_RUN)
  186. #if   (TOTAL_DATA_SIZE == 1200)
  187. #define PROFILE_RUN     1
  188. #elif (TOTAL_DATA_SIZE == 2000)
  189. #define PERFORMANCE_RUN 1
  190. #else
  191. #define VALIDATION_RUN  1
  192. #endif
  193. #endif


  194. #endif


  195. /************************ (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)