客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。
▲ 图1. STM32G474 架构
▲ 图2. ARM DSP Lib 目录结构
__attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] =
{
0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -
1.007763f, -0.742045f,
~~ 这里数组使用动态生成的float数据,数据量较大,略
-0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
0.650667f, 2.080061f
};
__attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
{
-2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -
1.126352f, -0.815002f,
~~ 这里数组使用动态生成的float数据,数据量较大,略
-0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
};
__attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];
float32_t* pA;
float32_t* pB;
float32_t* pR;
/* Private user code --------------------------------------------------*/
/* USER CODE BEGIN 0 */
void test_normal_mul(uint32_t kLoops, float32_t*
pSrcA, float32_t* pSrcB, float32_t*
pResult, uint32_t lenVector)
{
for (uint32_t j = 0; j < kLoops; j++)
{
pA = pSrcA;
pB = pSrcB;
pR = pResult;
for (uint32_t i = 0; i < lenVector; i++)
{ *pR++ = (*pA++) * (*pB++) ;
}
}
}
#if defined (__FPU_USED) && (__FPU_USED == 1U)
/* Use arm dsp lib to test basic operation Multiply, FPU enabled */
void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t*
pResult, uint32_t lenVector)
{
for (uint32_t j = 0; j < kLoops; j++)
{
pA = pSrcA; //Code alignment with the function without FPU
pB = pSrcB;
pR = pResult;
arm_mult_f32(pA, pB, pR, lenVector);
}
}
#endif
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
…
HAL_Delay(100);
/* USER CODE BEGIN 2 */
test_normal_mul(10, testInputA, testInputB, testResult, 1024);
test_normal_mul(10, testInputA, testInputB, testResult, 1024);
#if defined (__FPU_USED) && (__FPU_USED == 1U)
// Multiply calculation with arm dsp lib
test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
RW_IRAM1 0x20000000 0x00014000 { ; RW data
(+RW +ZI)
}
RW_IRAM2 0x20014000 0x00004000 {
*(.TEST_INPUT_A)
*(.TEST_INPUT_B)
*(.TEST_RESULT_D)
}
RW_CCM 0x20018000 0x00008000 {
}
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。
nStart = DWT->CYCCNT;
~~~需测试执行时间的代码~~~
nStop = DWT->CYCCNT;
然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
10 X 1024次浮点乘
增加--loop_optimization_level=2 编译选项
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
使用loop_optimization_level=2, 常规代码使用KEIL compiler V5编译结果与 arm DSP Lib 的核心汇编基本相同。如果不使用loop_optimization_level=2编译选项, 则可以看到其主要区别在于KEIL Compiler V5 与ARM库对loop的unroll 处理程度不 同。在实际应用时,需要根据应用自身需求判断是否需要使用ARM DSP Lib,基本上 ARM DSP Lib是很高效的。
END