嵌入式系统的开发,软件的运行稳定可靠是非常重要的。在芯片中,软件是没有质量的,但软件的质量可以决定一颗芯片的成败。芯片设计中,性能能否满足设计要求,除了硬件设计、软硬件配合的设计技巧,对于软件来说,编程的一些技术和技巧同样重要。
本文讲述我在芯片固件开发过程中使用的一些编程调试技巧。针对在嵌入式系统开发中常见的问题,如实时系统下的同步问题,动态内存分配的内存泄漏问题,如何在编程阶段预防BUG出现,调试阶段如何及时发现问题和定位问题。总结下经验,目的是开发一个稳定运行的固件,提高开发效率,提高运行性能。
一 编程调试技巧
(一) 动态内存分配还是静态内存分配?
嵌入式系统开发中,动态内存分配和静态内存分配各有利弊,动态内存分配灵活方便,但占用额外内存资源,调用时性能有损失,存在内存碎片问题。
在无线芯片固件开发中,在两者的使用上主要考虑一下几点:
- CPU性能
- 可用内存大小
- 需要使用内存分配的调用频率
- 性能影响
- 考虑malloc函数调用的开销。所有的收发通路上对数据帧的处理、管理使用的数据结构都用静态内存分配的方式。目的是为了减少处理时间。
- l站点管理,密钥管理,SDIO接口传递的外部命令,向上传送的SDIO事件管理采用动态内存分配的方式。这些调用和数据帧收发处理次数相比不是一个数量级,有的是偶尔调用。
- 一次性分配,存在于软件整个生命周期的数据结构变量采用静态内存分配。
(二) 使用ARM C库还是自己写一个?
嵌入式系统开发中经常会使用到C库的一些函数,如malloc,free,memcpy,memset,printf,是自己写一个呢还是利用ARM开发工具提供的C库呢?
我的习惯和建议是最好使用ARM的C库。优势就是
- 使用方便,减少开发周期
- ARM的C库性能会更好。
我以memcpy为例,网上有篇分析arm memcpy汇编代码的文章,ARM公司写的代码为什么更优化,性能更好呢?它主要考虑了以下几点:
- 源地址和目的地址的首地址的字节对齐问题
- 拷贝字节长度
- 末尾字节的对齐问题。
- 尽量word拷贝
- 尽量利用arm的批量拷贝汇编指令。
所以我不会去另外写个memcpy函数。使用memcpy时只要注意,4-8个长的采用赋值方式效率会高些。或者不影响性能的情况下怎么用都无所谓。
ARMC库有两种库:标准库和MicroC库。后者是非线程安全,在裸系统下使用。前者是线程安全的,可以在实时系统下使用。对malloc的使用用户需要实现一个保护和释放保护的函数,供C库使用。防止多线程调用malloc函数出现的同步问题。
(三) 如何预防和发现内存泄漏
使用动态内存分配,系统就可能出现内存泄漏,内存使用溢出,内存重复释放等问题,如果直接使用malloc和free,很难发现这样的BUG。在编程阶段,重定义malloc和free函数可以及时发现和定位这些问题。让程序去发现问题,而不是自己去找问题或者根本不知道有问题。
重定义的malloc采用双向链表管理所有动态分配的内存。下面是管理内存使用的数据结构:
每次内存分配分配如上大小的内存,包括三个部分,黄色部分为MEMORY_BLOCK数据结构,灰色部分为实际使用内存区,红色部分(4个字节)为尾部标记。
MEMORY_BLOCK保存了双向链表,分配的文件名指针和文件行号,分配的长度和头部标记。