本帖最后由 Litthins 于 2022-2-11 18:32 编辑
#技术资源#
N32G435是国民技术新推出的通用型MCU,采用ARM Cortex-M4内核+FPU,单周期硬件乘除法,最高主频108MHz。数据手册中使用Dhrystone作为性能评价指标(135DMIPS);却没有CoreMark的相关数据。那么N32G435能跑多少分?本文介绍使用Keil将CoreMark移植到N32G43XCL-STB开发板的一般方法。
第一部分 工程创建 N32G435的支持包需要单独安装,目前版本为0.9.2,pack全称“Nationstech.N32G43x_DFP.0.9.2.pack”,安装pack后使用Keil创建新工程,选择N32G435CB,如下图所示。
新建工程下是没有资源文件的,需要手动添加。依赖文件、中间件和参考例程打包在压缩文件Nationstech.N32G43x_Library中,目前的版本是1.1.0,全称“Nationstech.N32G43x_Library.1.1.0.7z”。解压后找到firmware\CMSIS\core、firmware\CMSIS\device和firmware\n32g43x_std_periph_driver,依次复制到工程目录下,从projects\n32g43x_EVAL\examples中获取main.c\main.h\n32g43x_it.c和n32g43x_it.h文件。
完成资源整理后,打开工程配置选项,设置编译器版本为V6.16,勾选使用MicroLIB。
点选C/C++选项卡,在预处理符号中添加:N32G43X, USE_STDPERIPH_DRIVER, USE_FULL_ASSERT
采用-Ofast优化,勾选优化选项中的Link-Time Optimization,资源包含路径可参考下图。
打开调试选项卡,右侧调试器选择CMSIS-DAP Debugger。点击Settings进入调试器配置页面。
在配置页面中选择CMSIS-DAP-NSLink,点击OK即可。至此新工程创建完成。
第二部分 工程测试 CoreMark的测试结果最终表示为Iterations/Sec,也就是每秒迭代数,因此需要为其提供计时功能。这里使用TIM2以每1ms产生一个中断,控制PC15反转电平,使用虚拟示波器观察波形以测试工程配置是否正确。设置系统时钟为108MHz,APB1四分频27M,注意如果APB1的分频系数大于1,则TIM2的时钟频率加倍,即TIM2时钟为54M。
时钟初始化代码:
RCC_ConfigPclk1(RCC_HCLK_DIV4);
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM2, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);
定时器初始化代码:
void TIM_CFG(void)
{
TIM_TimeBaseStructure.Period = 1000-1;
TIM_TimeBaseStructure.Prescaler = 54-1;
TIM_TimeBaseStructure.ClkDiv = 0;
TIM_TimeBaseStructure.CntMode = TIM_CNT_MODE_UP;
TIM_InitTimeBase(TIM2, &TIM_TimeBaseStructure);
TIM_ConfigInt(TIM2, TIM_INT_UPDATE, ENABLE);
}
设置定时器中断:
void NVIC_CFG(void)
{
NVIC_InitType NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
在虚拟示波器中观察输出波形,频率为500Hz,即中断每1ms触发1次,工程测试成功。
第三部分 CoreMark移植 CoreMark是衡量嵌入式系统中微控制器性能的基准。通过列表处理、矩阵处理、状态机和CRC等算法测试系统性能。测试软件是开源的,可以在官方网站EEMBC’s CoreMark®找到。网页截图如下。
解压下载好的压缩包,得到文件结构如下图左侧所示。将simple文件夹下的core_portme.c和core_portme.h复制到USER文件夹中。新建COREMARK文件夹,将core_list_join.c、core_main.c、core_matrix.c、core_state.c、core_util.c、coremark.h等文件复制进去。
完成文件复制后,新文件分组可参考下图,注意工程路径中也要添加COREMARK文件夹。
由于core_main.c文件已提供了main()函数,因此原main()函数将无法使用。作为替代,新main()函数执行时会调用core_portme.c中的portable_init()函数,移植时需要将原工程中MCU初始化代码复制到portable_init()函数中并删除原main.c文件。 相关初始化函数如下,设置时钟相关:
void RCC_CFG(void)
{
RCC_ConfigPclk1(RCC_HCLK_DIV4);
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM2, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1,ENABLE);
}
设置串口关联的GPIO:
void GPIO_CFG(void)
{
GPIO_InitType GPIO_InitStructure;
GPIO_InitStruct(&GPIO_InitStructure);
/* Configure USARTx Tx as alternate function push-pull */
GPIO_InitStructure.Pin = GPIO_PIN_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF4_USART1;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
/* Configure USARTx Rx as alternate function push-pull and pull-up */
GPIO_InitStructure.Pin = GPIO_PIN_10;
GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF4_USART1;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
}
设置串口:
void USART_CFG(void)
{
USART_StructInit(&USART_InitStructure);
USART_InitStructure.BaudRate = 115200;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
/* Configure USARTx */
USART_Init(USART1, &USART_InitStructure);
/* Enable the USARTx */
USART_Enable(USART1, ENABLE);
}
设置定时器,每1ms一个中断:
void TIM_CFG(void)
{
/* Time base configuration */
TIM_TimeBaseStructure.Period = 1000-1;
TIM_TimeBaseStructure.Prescaler = 54-1;
TIM_TimeBaseStructure.ClkDiv = 0;
TIM_TimeBaseStructure.CntMode = TIM_CNT_MODE_UP;
TIM_InitTimeBase(TIM2, &TIM_TimeBaseStructure);
/* TIM2 enable update irq */
TIM_ConfigInt(TIM2, TIM_INT_UPDATE, ENABLE);
}
设置定时器中断:
void NVIC_CFG(void)
{
NVIC_InitType NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
printf重定向:
int fputc(int ch, FILE* f)
{
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXDE) == RESET)
;
return (ch);
}
core_portme.c中,与定时相关的函数有以下三个,在start_time()里实现定时器启动功能,在stop_time()里实现定时器停止功能,在get_time()中获取中断计数值,相关实现如下:
unsigned long time_ms_ticks=0;
void start_time(void)
{
TIM_Enable(TIM2, ENABLE);
}
Void stop_time(void)
{
TIM_Enable(TIM2, DISABLE);
}
CORE_TICKS get_time(void)
{
return time_ms_ticks;
}
void TIM2_IRQHandler(void)
{
TIM_ClrIntPendingBit(TIM2, TIM_INT_UPDATE);
time_ms_ticks=time_ms_ticks+1;
}
告知程序,每1000个tick为1s,修改以下宏定义:
#define EE_TICKS_PER_SEC 1000
注释部分不使用的代码。以下代码位于core_portme.c中,需要注释掉:
//#define NSECS_PER_SEC CLOCKS_PER_SEC
//#define CORETIMETYPE clock_t
//#define GETMYTIME(_t) (*_t = clock())
//#define MYTIMEDIFF(fin, ini) ((fin) - (ini))
//#define TIMER_RES_DIVIDER 1
//#define SAMPLE_TIME_IMPLEMENTATION 1
//static CORETIMETYPE start_time_val, stop_time_val;
运行CoreMark时,测试时间低于10s会抛出错误,经测试N32G435设置迭代次数为3000较为合适:
volatile ee_s32 seed4_volatile = 3000;
打印测试结果时,编译器优化等级在core_portme.h中通过宏COMPILER_FLAGS定义并打印。对应工程设置,修改如下:
#define COMPILER_VERSION "Clang 13.0.0"
#define COMPILER_FLAGS "-Ofast"
编译并下载程序,测试结果如下图所示:
N32G435,108MHz,267.35分!参考N32G45x系列144MHz,180DMIPS,CoreMark跑分480分;N32G435在108MHz下135DMIPS,初略估算,135/180*108/144*480=270分,与测试结果接近。测试工程已通过附件分享,如有需要可下载参考。
|