CoreMark是MCU性能测试的业内标准之一,在EEMBC的官方性能公布网页,查看公布的不同厂家的MCU的跑分结果
https://www.eembc.org/coremark/scores.php
其中H5系列的性能显示CoreMark数值在250MH主频下为1023

CoreMark的源码是公开的,可以很方便地移植
https://github.com/eembc/coremark
1、CubeMX生成Keil工程
CoreMark程序移植需要设置MCU主频为最高,并设置用于输出测试结果的串口。CubeMX中创建NUCLEO-H5E5ZJ的工程,并勾选BSP中的外设初始化。


设置MCU的HCLK为最大主频

设置CubeMX生成的工程类型为Keil,并调整Stack Size的大小为0x2000,满足CoreMark程序的运行需要。

2、CoreMark代码移植
CoreMark在MCU中移植时需要使用到以下文件,将以下文件添加到生成的Keil工程中。移植的注意事项参考仓库中的barebones_porting.md说明进行即可。
│ coremark.h
│ core_list_join.c
│ core_main.c
│ core_matrix.c
│ core_state.c
│ core_util.c
├─barebones
│ core_portme.c //硬件适配
│ core_portme.h //配置文件
│ cvt.c
│ ee_printf.c //串口输出
2.1 修改ee_printf.c
ee_printf.c中修改uart_send_char,将字符串输出定向到BSP驱动中的调试串口。
void
uart_send_char(char c)
{
HAL_UART_Transmit(&hcom_uart [COM1], (uint8_t *) &c, 1, COM_POLL_TIMEOUT);
}

2.2 修改 core_portme.h
根据需要修改宏定义
| Preprocessing macro |
Value |
| HAS_FLOAT |
0 or 1 based on the processor/device |
| HAS_TIME_H |
0 |
| USE_CLOCK |
0 |
| HAS_STDIO |
1 |
| HAS_PRINTF |
1 |
根据编译的优化选项设置字符串和调试器信息,用于最后的结果输出
/* Definitions : COMPILER_VERSION, COMPILER_FLAGS, MEM_LOCATION
Initialize these strings per platform
*/
#define FLAGS_STR "-mcpu=cortex-m33 -Omax"
#ifndef COMPILER_VERSION
#ifdef __GNUC__
#define COMPILER_VERSION "GCC"__VERSION__
#else
#define COMPILER_VERSION "Please put compiler version here (e.g. gcc 4.1)"
#endif
#endif
#ifndef COMPILER_FLAGS
#define COMPILER_FLAGS \
FLAGS_STR /* "Please put compiler flags here (e.g. -o3)" */
#endif
#ifndef MEM_LOCATION
#define MEM_LOCATION "STACK"
#endif
设置 MAIN_HAS_NOARGC
#ifndef MAIN_HAS_NOARGC
#define MAIN_HAS_NOARGC 1
#endif
2.3 修改 core_portme.c
定义Benchmark的迭代次数与定时器相关的宏定义
#define ITERATIONS 12000
#define SYSTICK_LOAD 0xFFFFFF
#define SYSTICK_START 7
#define OVERFLOW_VAR 0xFFFFFFFF
uint8_t start=0;
实现barebones_clock函数,并修改相关的时钟源参数和时钟分频宏定义的值。
#define CLOCKS_PER_SEC SystemCoreClock
#define NSECS_PER_SEC CLOCKS_PER_SEC
#define GETMYTIME(_t) (*_t=barebones_clock())
#define MYTIMEDIFF(fin,ini) ((fin)-(ini))
#define TIMER_RES_DIVIDER 1000
#define EE_TICKS_PER_SEC (NSECS_PER_SEC / TIMER_RES_DIVIDER)
#define SAMPLE_TIME_IMPLEMENTATION 1
CORETIMETYPE barebones_clock() {
extern __IO uint32_t uwTick ;
if(start)
{
SysTick->CTRL = 0;
uint64_t tmp1 = ((uint64_t)uwTick * SYSTICK_LOAD);
tmp1 += (SYSTICK_LOAD - SysTick->VAL);
tmp1 /=TIMER_RES_DIVIDER;
if (tmp1 > OVERFLOW_VAR)
{
/* if end time = start time there is an overflow*/
return 0;
}
else
{
return (uint32_t)tmp1;
}
}
else
{
start=1;
uwTick = 0;
SysTick->CTRL = 0;
SysTick->VAL = 0;
SysTick->LOAD = SYSTICK_LOAD;
SysTick->CTRL = SYSTICK_START;
}
return 0;
}
修改portable_init如下,由于串口和用于计时的SysTick在程序初始化时完成,无需在此处进行初始化
void
portable_init(core_portable *p, int *argc, char *argv[])
{
(void)argc; // prevent unused warning
(void)argv; // prevent unused warning
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;
}
2.4 修改 core_main.c
修改core_main.c中的main函数名称,避免main函数重定义。
#if MAIN_HAS_NOARGC
MAIN_RETURN_TYPE
coremain(void)
{
int argc = 0;
char *argv[1];
#else
2.5 main.c中添加代码调用
main.c中添加coremain函数声明,用于CoreMark软件栈的入口。
extern int coremain(void);
在main函数中添加该函数的调用。
printf("CoreMark Start\r\n");
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
coremain();
printf("CoreMark End\r\n");
调整C/C++的编译选项,如下


编译并烧录到开发板,得到的CoreMark值为1010,接近公布的H5系列的1023。
