如何优化生成文件中的Code和RAM大小
如果产品的生产商能为初学者提供一些入门,那就太好了。blinky示例程序是一个很好的起始点。便利总要付出代价的,blinky示例的代码就是切换GPIO引脚的代码大小被夸大了。对于只有少量RAM和存储器的器件,代码可能可能会需要关注:如果blinky代码占据的空间那么大,我的应用程序是否会适合该器件呢?不用担心:blinky代码(以及其他任何项目)都可以轻松裁剪。
在本篇文章中,我将以blinky项目为例展示裁剪技巧,也可以应用于任何其他类型的项目。本文使用的是NXP LPC845开发板:
恩智浦LPC845-BRK开发板
Blinky项目
本文使用基于Eclipse的NXP MCUXpresso IDE。首先使用供应商默认设置新建了一个blinky项目:
blinky项目
blinky项目可以实现LED闪烁,这对于任何项目都是一个很好的入门。制作这个相当小的项目的代码大小如下所示:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 10536 B 64 KB 16.08%
- SRAM: 2424 B 16 KB 14.79%</font>
复制代码 这些信息也以这种方式显示在控制台中,分为text、data和bss:
- <font size="3" face="微软雅黑"> text data bss dec hex filename
- 10532 4 2420 12956 329c lpc845breakout_led_blinky.axf</font>
复制代码 10K的代码量看起来有点夸张。我们将在下一步中对此进行调整。
代码大小信息
查看器件上正在使用空间的正常方式是检查链接器映射文件(* .map):
链接器映射文件
但是该map文件相当难以阅读,对于专家而言则信息更多:它列出了带有地址和大小的部分:
链接器映射文件内容
使用MCUXpresso IDE V11,有一个不错的“Image Info(映像信息)”视图,从本质上来说,它是一个更好的查看器,用于查看map文件信息:
Image Info视图
我可以对数据进行过滤和排序,这使我了解了代码和数据使用了多少空间:
Image Info Memory Content
当然,它需要有关应用程序应该做什么的一些知识。我总是查看视图中的项目列表,以查看是否有我所不希望的内容:也许应用程序使用的是可以删除的内容。
源代码
对于简单的blinky来说,这相当小。第一件事是检查程序在做什么。 main.c如下:
- <font size="3" face="微软雅黑">/*
- * Copyright 2017 NXP
- * All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
- #include "board.h"
- #include "fsl_gpio.h"
-
- #include "pin_mux.h"
- /*******************************************************************************
- * Definitions
- ******************************************************************************/
- #define BOARD_LED_PORT 1U
- #define BOARD_LED_PIN 2U
-
- /*******************************************************************************
- * Prototypes
- ******************************************************************************/
-
- /*******************************************************************************
- * Variables
- ******************************************************************************/
- volatile uint32_t g_systickCounter;
-
- /*******************************************************************************
- * Code
- ******************************************************************************/
- void SysTick_Handler(void)
- {
- if (g_systickCounter != 0U)
- {
- g_systickCounter--;
- }
- }
-
- void SysTick_DelayTicks(uint32_t n)
- {
- g_systickCounter = n;
- while (g_systickCounter != 0U)
- {
- }
- }
-
- /*!
- * @brief Main function
- */
- int main(void)
- {
- /* Define the init structure for the output LED pin*/
- gpio_pin_config_t led_config = {
- kGPIO_DigitalOutput,
- 0,
- };
-
- /* Board pin init */
- BOARD_InitPins();
- BOARD_InitBootClocks();
- BOARD_InitDebugConsole();
-
- /* Init output LED GPIO. */
- GPIO_PortInit(GPIO, BOARD_LED_PORT);
- GPIO_PinInit(GPIO, BOARD_LED_PORT, BOARD_LED_PIN, &led_config);
-
- /* Set systick reload value to generate 1ms interrupt */
- if (SysTick_Config(SystemCoreClock / 1000U))
- {
- while (1)
- {
- }
- }
-
- while (1)
- {
- /* Delay 1000 ms */
- SysTick_DelayTicks(1000U);
- GPIO_PortToggle(GPIO, BOARD_LED_PORT, 1u << BOARD_LED_PIN);
- }
- }</font>
复制代码 代码是初始化引脚、时钟、设置SysTick计时器,然后使用Systick计数器延迟闪烁周期来循环执行“闪烁”。
半主机Semihosting和printf
接下来要看的是是否有任何半主机或printf()。与“标准” newlib或较小标准的newlib-nano相比,该项目使用的是“优化库”“ Redlib”:
Redlib
不过,该库可能会增加代码大小,因为它使用的是半主机(通过调试器发送消息)。查看“内存”视图,可以看到直接或间接需要的所有这些标准I / O函数:
stdio函数
拥有该功能的所有钩子仅在使用时才有意义,而blinky则不使用。因此,不使用这种半主机和所有未使用的标准I / O意味着要使用“none”变体:
没有标准I / O的库
这样我们就可以得到以下:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 3372 B 64 KB 5.15%
- SRAM: 2208 B 16 KB 13.48%</font>
复制代码 调试和NDEBUG
下一步是检查编译器定义是否列出了DEBUG。以下是示例:
调试定义
有了该定义集,SDK和示例驱动程序中会包含许多额外的代码,这些代码可通过“ assert()”宏检查是否有合适的值:
同样,这里的图像信息视图很有用:它向我显示了使用assert()的所有位置:
assert用法
在代码中断言以及早发现编程错误实际上是一个好习惯。但是所有的assert()代码确实加起来了。要关闭多余的代码(和安全带!),我将宏更改为NDEBUG:
NDEBUG
这样我们可以实现:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 3144 B 64 KB 4.80%
- SRAM: 2208 B 16 KB 13.48%</font>
复制代码 中断(Interrupt)和向量(Vector)
同样,“图像信息”视图是一个很好的起点。我正在检查使用的中断。 Blinky正在使用预期的SysTick中断。但是仍然使用UART中断吗?
使用的中断
大多数中断都实现为“弱”:实现为默认/空,可以被应用程序覆盖。但是UART没有意义,因为blinky不使用任何UART通信?
事实证明,NXP SDK默认情况下启用了UART事务API:
UART事务API设置
事务性API允许在通信块/事务中发送/接收UART数据。但是,我们不需要在blinky之间就关闭它,所以我们将其关闭:
关闭UART事务性API
这使:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 2964 B 64 KB 4.52%
- SRAM: 2184 B 16 KB 13.33%</font>
复制代码优化
到目前为止,我已经剥离了不需要或未使用的函数。接下来,我可以打开编译器优化。默认情况下,项目设置为-O0: 编译器优化
-O0表示没有优化:代码简单明了且易于调试。
-O1主要优化函数的进入/退出代码,并且能够在不真正影响调试的情况下减小代码大小。在此示例中,它将代码大小减少了一半!
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1540 B 64 KB 2.35%
- SRAM: 2184 B 16 KB 13.33%</font>
复制代码 -O2优化更多,并尝试将其尽可能多地保留在寄存器中。由于应用程序中的功能很小,因此改进并不大:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1516 B 64 KB 2.31%
- SRAM: 2184 B 16 KB 13.33%</font>
复制代码 -O3通过额外的内联来最优化。 -O3以速度为目标,因此难怪代码大小会再次增加:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1792 B 64 KB 2.73%
- SRAM: 2184 B 16 KB 13.33%</font>
复制代码 代码大小优化的最佳选择是-Os(针对大小进行优化):
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1456 B 64 KB 2.22%
- SRAM: 2184 B 16 KB 13.33%</font>
复制代码
现在看起来很合理!当然,现在有很多方法可以消除“blinky”的问题,但是对于实际应用而言,所有适当的地方(启动代码、时钟和GPIO初始化)都是有意义的,所以我现在就在这里结束。
RAM:堆和堆栈
看起来不正确的是SRAM的使用情况。 “堆”使用了大量块:
堆内存使用情况
该堆用于动态内存分配(malloc())。嵌入式编程的一般规则是避免使用它。但这是默认设置。可以在链接器设置中将其关闭:该演示使用1K进行堆和栈。由于我没有使用malloc(),因此可以将堆大小设置为0x0。对于真正取决于应用程序的保留堆栈。在ARM Cortex上,MSP用于启动/主程序和中断。 0x100(256字节)应该足够满足blinky项目。
堆和堆栈大小
这样可以实现:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1456 B 64 KB 2.22%
- SRAM: 392 B 16 KB 2.39%</font>
复制代码 如果要进一步减小堆栈大小,我可以查看“调用图”信息,该信息为我提供了有关已使用多少堆栈空间的信息:
具有堆栈大小的调用图
MTB
使用RAM空间的还有一件事:MTB缓冲区。Micro Trace Buffer(微型跟踪缓冲区)用于跟踪,这可能非常有用。可以使用宏禁用缓冲区:
这样可以实现:
- <font size="3" face="微软雅黑">Memory region Used Size Region Size %age Used
- PROGRAM_FLASH: 1456 B 64 KB 2.22%
- SRAM: 136 B 16 KB 0.83%</font>
复制代码 总结
供应商提供示例很好:它们给了我一个很好的起点。它们没有优化,这是有意的。但是它们可能带有我不需要的功能。知道通过切断功能或调整设置来优化应用程序的不同方法对于优化RAM和FLASH的使用非常有用。在本文中,我展示了如何将blinky降低到大约1KB闪存和大约136个字节的SRAM。当然,这全部取决于功能和用法,但是我认为这是为应用程序添加额外功能的一种相当合理的状态。
我希望这些技巧可能对您的项目有用。
作者:阿哲 文章出处:点击
|