查看: 1172|回复: 1

【瑞萨RA4系列开发板体验】CoreMark移植完全指南

[复制链接]

116

主题

134

帖子

3790

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3790
发表于 2023-2-3 10:43:45 | 显示全部楼层 |阅读模式
【瑞萨RA4系列开发板体验】CoreMark移植完全指南——UART输出和SysTick计时的应用作者:xusiwei1236


修改记录:
  • 2022-11-27 初版开始
  • 2022-12-03 初版完成
一、CoreMark简介
什么是CoreMark?
来自[color=rgb(12, 147, 228) !important]CoreMark首页的解释是:
CoreMark is a simple, yet sophisticated benchmark that is designed specifically to test the functionality of a processor core. Running CoreMark produces a single-number score allowing users to make quick comparisons between processors.
翻译一下就是:
CoreMark是一个简单而又精密的基准测试程序,是专门为测试处理器核功能而设计的。运行CoreMark会产生一个“单个数字”的分数,(从而)允许用户在(不同)CPU之间进行快速比较。
简单来说,就是一个测试CPU性能的程序,类似PC上的Cinebench、CPU-Z之类的CPU性能测试工具。
了解了CoreMark是什么之后,接下来我们尝试在RA-Eco-RA4M2-100PIN开发板上跑一下CoreMark,看看分数是多少。
接下来就可以开始进行CoreMark移植了,为了让移植步骤清晰明确,这里我把移植分为两大部分:
  • 基础功能支持
  • CoreMark移植
二、基础功能支持
CoreMark是一个基准测试功能程序,在MCU上运行它通常依赖两个基础功能:
  • 打印输出
    • 一般使用print打印,输出到UART串口(使用PC接收UART输出的内容)
  • 精准计时
    • 一般使用SysTick计时,精度通常为毫秒级别

2.1 创建RASC项目
首先是RASC创建项目,名为RA4M2_CoreMark,如下图所示:
接着,选择MCU、TrustZone、RTOS,和上一篇一样即可生成Keil项目。
2.2 确认UART引脚
查阅开发板原理图,找到USB转串口部分:
可以看到,USB转串口芯片CH340G的TXD/RXD分别连接到P110、P109.
查阅用户手册:
可以知道,P109、P110引脚需要将功能设置为TXD9、RXD9才可以正常和PC通信,也就是SCI9。
2.3 打开RASC配置
如果RASC没有关闭,则可以直接进行配置,无需额外操作。
如果RASC已经关闭,可以通过Manage Run-Time Environment菜单,打开已有配置,具体步骤如下图所示:
以上截图自RASC帮主文档,说的比较清楚,图文并茂,这里就不翻译了。
2.4 配置UART引脚功能
默认创建的RASC项目中,P109、P110为JTAG功能,如下图所示:
因此,首先需要关闭JTAG功能,或者修改为SWD模式;否则,设置为UART功能时会报错。
将调试模式从JTAG改为SWD,具体操作如下。
  • 在RASC中,切换到Pins标签页;
  • 找到SystemEBUG -> DEBUG0,将其设置修改为SWD,引脚修改为P108、P300,如下图所示:

  • P108、P300引脚也正是和开发板原理图相对应的。

继续找到Connectivity:SCI -> SCI9,将其设置修改如下图所示:
2.5 添加UART HAL配置
在RASC中,切换到Stacks标签页,依次点击“New Stack”->“Connectivity”->“UART”,添加一个uart组件,如下图所示:
鼠标选中之后,在Properties视图中,修改如下几个属性:
修改后Ctrl+S保存。
点击“Generate Project Content”按钮,生成Keil项目。
2.6 添加printf输出到UART支持
在Keil中,实现UART打印到printf需要重新实现C标准库的putc函数,具体参考下面两个资料:
主要实现代码如下:
  1. #include <stdio.h>

  2. #include "r_sci_uart.h"
  3. #include "hal_data.h"

  4. volatile bool uart_tx_done = false;

  5. void hal_uart9_init()
  6. {
  7.     R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
  8. }

  9. int fputc(int ch, FILE* f)
  10. {
  11.     (void) f;

  12.     uart_tx_done = false;

  13.     R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)&ch, 1);
  14.    
  15.     while (uart_tx_done == false);

  16.     return ch;
  17. }

  18. void debug_uart9_callback(uart_callback_args_t* p_args)
  19. {
  20.    switch (p_args->event)
  21.    {
  22.       case UART_EVENT_RX_CHAR:
  23.             break;
  24.       case UART_EVENT_TX_COMPLETE:
  25.             uart_tx_done = true;
  26.             break;
  27.       default:
  28.             break;
  29.    }
  30. }
复制代码
这里使用到的R_SCI_UART_Open和R_SCI_UART_Write是FSP的API,可以查阅FSP API参考,找到说明(或者查看头文件的注释):
这样,在hal_entry中调用了hal_uart9_init之后,就可以使用printf输出到串口了。
2.7 添加SysTick计时支持
CoreMark依赖计时功能,在基于ARM Cortex-M系列内核的MCU上,一般使用SysTick进行计时;例如RA4M2是基于Cortex-M33的。
而SysTick属于Cortex-M33的外设,关于SysTick的具体说明可以参考野火的一篇教程。
而在CMSIS(Cortex-M Software Interface Standard)软件包中,已经对Cortex-M系列内核自带的外设的操作接口进行了封装,我们在使用SysTick时一般只需要在代码中:
  • 调用SysTick_Config函数设置SysTick中断频率;
  • 编写SysTick_Handler函数实现SysTick中断处理;
主要实现代码如下:
  1. #include <stdint.h>
  2. #include "hal_data.h"

  3. #define TICKS_PER_SECOND 1000

  4. volatile uint32_t g_tick_count = 0;

  5. void hal_systick_init()
  6. {
  7.     SysTick_Config(TICKS_PER_SECONDS);
  8. }

  9. void SysTick_Handler(void)
  10. {
  11.     g_tick_count += 1;
  12. }

  13. uint32_t hal_systick_get()
  14. {
  15.     return g_tick_count;
  16. }
复制代码
这里,hal_systick.h中定义了宏TICKS_PER_SECOND,值为1000,也就是每秒1000次SysTick中断。
这样,在hal_entry中调用了hal_systick_init()之后,就可以使用hal_systick_get进行计时了。
使用hal_systick_get和TICKS_PER_SECOND进行计时,类似于使用标准库time.h中的clock()和CLOCKS_PER_SEC,如下:
  1. uint32_t start = hal_systick_get();

  2. // ...
  3. // 一些需要计时的代码
  4. // ...

  5. uint32_t end = hal_systick_get();
  6. float cost_secs = (end - start) / (float) TICKS_PER_SECOND;
  7. printf("time cost: %f seconds.", cost_secs);
复制代码
当然,你也可以在hal_entry.c中添加可以通过如下代码,对上述代码进行测试:
  1. hal_uart9_init();
  2.     printf("uart9 init done!\n");

  3.     hal_systick_init();
  4.     while (1) {
  5.         printf("ticks: %d\n", hal_systick_get());
  6.         R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
  7.     }
复制代码
三、CoreMark移植
3.1 添加CoreMark源码
将代码下载下来之后,将其中的如下文件和目录拷贝到keil项目的src子目录下:
拷贝完成后,打开Keil,在Project视图中,通过“Add Exists Files to Group”菜单将刚刚拷贝的文件添加到项目中。
添加完成后,Project视图如下图所示:
3.2 修改CoreMark源码
CoreMark源码本身已经为了移植做了充分考虑了,移植到新的MCU裸机或OS上一般只需要修改很少代码即可,下面分别介绍。
3.2.1 修改core_portme.c文件
core_portme.c文件中,需要修改的是计时的几个宏定义,具体如下:
  1. // 注释(或者删除)原来的这三个宏定义
  2. //#define CORETIMETYPE               clock_t
  3. //#define GETMYTIME(_t)              (*_t = clock())
  4. //#define EE_TICKS_PER_SEC           (NSECS_PER_SEC / TIMER_RES_DIVIDER)

  5. // 添加 以下新的宏定义:
  6. #include "hal_systick.h"
  7. #define CORETIMETYPE               uint32_t
  8. #define GETMYTIME(_t)              (*_t = hal_systick_get())
  9. #define EE_TICKS_PER_SEC              TICKS_PER_SECOND
复制代码
3.2.2 修改coremark.h文件
core_portme.h文件中,需要新增两个宏定义:
  1. #define ITERATIONS 4000    // 这个值需要保证能够运行至少10秒,可以先写一个值,运行不足10秒会报错,再回来修改
  2. #define FLAGS_STR "-Ofast" // 这个值根据实际的编译优化选项进行填写,在最终输出种原样输出,实际上只要是字符串就行,无关紧要
  3. #define MAIN_HAS_NOARGC 1  // coremark main不使用返回值
复制代码
这里我们将FLAGS_STR定义为-Ofast,编译选项也相应的修改为-Ofast。
3.3 解决编译问题
完成上述修改之后,开始编译可能会遇到如下问题:
  • 头文件找不到的问题;
  • main重复定义的问题;
下面分别介绍如何解决。
首先是—— 头文件找不到的问题 ,Keil中需要通过菜单将对应文件所在目录添加到搜索列表中,具体步骤为:
  • 通过Project视图,右击Target1之后,选中菜单“Options for Target ...”;
  • 在“Options for Target ...”界面中,选中 C/C++ 标签页;
  • 在 Include Paths 区域,点击右侧“...”;
  • 在弹出的“Floder Setup”界面中,将src和src/simple子目录添加上;
然后是—— main重复定义的问题 ,原因是FSP框架中已经定义了一个main函数。解决办法是,修改代码,具体修改为:
  • core_portme.h中,将MAIN_HAS_NOARGC宏定义代码行修改为:
    #define MAIN_HAS_NOARGC 1
  • core_main.c中,将main函数重命名为coremark_main:
  • coreamrk.h中,添加coremark_main声明的代码:

  1. #if MAIN_HAS_NOARGC
  2. MAIN_RETURN_TYPE
  3. coremark_main(void);
  4. #else
  5. MAIN_RETURN_TYPE
  6. coremark_main(int argc, char *argv[]);
  7. #endif
复制代码

  • hal_entry.c中,hal_entry函数中添加如下代码:
    • 声明void coremain_main();
    • 调用hal_systick_init();
    • 调用coremain_main();

3.4 解决栈溢出问题
完成上面的操作步骤后,CoreMark项目就可以正常编译了。如果此时直接运行,将会发现系统复位,无法正常运行;通过断点调试,可以发现是因为栈空间不足导致的。
默认情况下,CoreMark使用的是栈内存进行的计算,而RASC默认的栈空间大小为1024字节(0x400)。需要增大栈内存大小,才可以正常运行CoreMark。
具体设置位于RASC菜单的BSP->roperties界面,将栈空间修改为4096(或者0x1000),如下图所示:

四、CoreMark跑分
完成以上所有操作后,就可以在RA4M2上正常运行CoreMark程序了。CoreMark程序运行完成后,会输出具体跑分,如下图所示:
跑分为:333.694836
官方跑分,可以在CoreMark项目官网的Scores页面查到,具体如下图:
可以看到官方的跑分为398.30分。
五、本篇总结
本文所有代码,以及具体修改,见代码仓:https://gitee.com/swxu/ra4m2-coremark
代码仓中的提交记录也体现了移植步骤,感兴趣的小伙伴课可以去看每个提交记录的修改。
本篇记录了完整的将CoreMark移植到RA4M2系列MCU的操作步骤,以及遇到问题的解决方法。CoreMark依赖的两个基础功能为——输出和计时,因此本篇介绍首先介绍了如何在RA4M2上实现printf输出到UART;然后介绍了如何实现基于SysTick的计时,最后才介绍CoreMark移植相关的源码修改和编译、运行问题解决。
六、参考链接


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

3

主题

195

帖子

1168

积分

金牌会员

Rank: 6Rank: 6

积分
1168
发表于 2023-3-14 11:13:40 | 显示全部楼层

不错,学习一下
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

用户排行榜

RA助手

主题: 116帖子:134精华:0

RA_Lance

主题: 93帖子:136精华:9

lugl

主题: 52帖子:150精华:1

xujiwei263

主题: 18帖子:86精华:0

Juggernaut

主题: 12帖子:118精华:0

books咦

主题: 11帖子:11精华:2
快速回复 返回顶部 返回列表