介绍从AC5到AC6转型之路

一、说在前面的话
时间大约在2015年,arm第一次在 mdk 5.20 中引入了arm compiler 6(那时候的版本是 6.9),正式拉开了arm官方编译器从第五版(armcc)到第六版(armclang)升级替换的序幕……
嵌入式行业的长尾效应是及其突出的,且不说都2022年了还有很多人在坚持 mdk4,即便是从“arm在2017年对外宣布停止维护 arm compiler 5”算起,如今5年过去了,坚持使用 armcc 的用户仍然不在少数。
arm compiler 5,也就是大家口中的 armcc,它很弱么?相对免费的工具链 arm gcc来说,它还是强很明显;但你要说它非常能打么?作为一个“理论上”收费的编译器,它甚至已经全方位落后于最新发布的“免费开源”编译器llvm embedded toolchain for arm 14.0.0(clang),更不用说现在的当红贵人arm compiler 6(armclang)了。
如果非要我给出一份“不负责任”的编译器性能对比的话,这是独属于我的答案:
arm gcc < armcc < clang < iar = 5000000) && (__armcc_version = 6010050)# define __is_compiler_arm_compiler_6__ 1#endif#undef __is_compiler_arm_compiler__#if defined(__is_compiler_arm_compiler_5__) && __is_compiler_arm_compiler_5__ || defined(__is_compiler_arm_compiler_6__) && __is_compiler_arm_compiler_6__# define __is_compiler_arm_compiler__ 1#endif借助它们的帮助,我们可以很容易的通过判断 __is_compiler_arm_compiler_5__ 和 __is_compiler_arm_compiler_6__ 的值是否为“1”来确定当前的编译器版本。
在只关心当前编译器是否为arm compiler,而不在乎它具体是哪个版本时,可以借助 __is_compiler_arm_compiler__ 来进行判断。
假设我们的代码只考虑支持 gcc、clang、iar、armcc和armclang,那么利用排除法,我们就可以轻松的判断当前编译环境是否是 gcc 或 llvm了:
#undef __is_compiler_llvm__#if defined(__clang__) && !__is_compiler_arm_compiler_6__# define __is_compiler_llvm__ 1#else//! ote for gcc# undef __is_compiler_gcc__# if defined(__gnuc__) && !( defined(__is_compiler_arm_compiler__)                             ||  defined(__is_compiler_llvm__)                                               ||  defined(__is_compiler_iar__))# define __is_compiler_gcc__ 1# endif//! @}#endif  
简单说一下这里的思路:
1、在排除了 arm compiler 6 的前提下,根据 __clang__ 来判断当前编译器是否为 llvm(即:__is_compiler_llvm__);
2、在排除了 llvm、arm compiler 和iar的前提下,根据 __gnuc__ 来判断当前编译器是否为 gcc
为了方便大家理解,下面介绍几个上述宏的应用场景:
如何在 arm compiler 6 下告知编译器 main() 函数不带输入参数
默认情况下(使用默认的 libc),arm compiler 6会认为 main() 函数是带有标准的输入参数的:
int main (int argc, char *argv[]);  
哪怕你强行把 main() 函数写成无需输入参数的情况,编译器也还是会准备好参数——而准备参数的过程很有可能会导致 hardfault(这里会涉及到semihosting的问题,比较头疼,暂时不表)。
为了解决这一问题,我们一般这么做:
#if __is_compiler_arm_compiler_6____asm(.global __arm_use_no_argv );#endif  
又因为 microlib 不存在该问题,因为我们可以根据(mdk会追加的一个宏)__microlib,来做一个小小的区分:
#if __is_compiler_arm_compiler_6__# ifndef __microlib__asm(.global __arm_use_no_argv );#   endif#endif  
也就是当且仅当我们使用 arm compiler 6,且不使用microlib的时候,通过专门的语法结构来告诉编译器:main() 函数没有传入参数。
如何关闭 semihosting
你有没有遇到过这样神奇的情景:在调试模式下,程序可以正常运行;一旦退出调试模式,系统就死机了,重新进入调试模式后,发现系统进入了hardfault。
恭喜你,这很可能就是(默认开启的)semihosting 在作怪。关于semihosting的内容,篇幅过大,不在本文讨论之列。
今天我们只介绍一下如何关闭它。
arm compiler 5和arm compiler 6关闭 semihosting的方法是不同的:
#if __is_compiler_arm_compiler_6__ __asm(.global __use_no_semihosting);#elif __is_compiler_arm_compiler_5__    #pragma import(__use_no_semihosting)#endif一旦关闭了 semihosting,arm compiler 6 就可能会报告类似如下的错误:error: l6915e: library reports error: __use_no_semihosting was requested, but _sys_exit was referenced简单解释下原因:arm compiler 6 依赖的一个函数 _sys_exit() 原本是用semihosting方式默认提供的,现在你把 semihosting 关闭了,所以你要负责到底。
知道了原因,解决方法也很简单——缺这个函数,我们提供一个就行:#if __is_compiler_arm_compiler_6__void _sys_exit(int ret){    (void)ret; while(1) {}}#endif类似的情况还会发生在一个叫 _ttywrch() 的函数上,我们可以如法炮制:/* 为 arm compiler 5 和 arm compiler 6 都添加这个空函数 */#if __is_compiler_arm_compiler__void _ttywrch(int ch){ arm_2d_unused(ch);}#endif   
如何解决使用 assert.h 引发的问题
很多代码都有使用 assert() 来截获错误的习惯,当我们使用 arm compiler 6 且开启 microlib的时候,由于 microlib并不提供对 assert() 底层函数的具体实现,当我们没有定义 ndebug 来关闭 assert() 时,会在链接阶段看到如下的编译错误:
error: l6218e: undefined symbol __aeabi_assert (referred from main.o).  
知道原因后,解决也很简单:既然microlib没提供实现,我们就自己提供一个好了:
#if __is_compiler_arm_compiler_6__ && defined(__microlib)void __aeabi_assert(const char *chcond, const char *chline, int werrcode) {    (void)chcond;    (void)chline;    (void)werrcode;     while(1) { __nop(); }}#endif   
既然上述这套 __is_compiler_xxxx__ 这么好用,我们可以从哪里获得呢?
目前已知的获取渠道包括但不限于:
从本文抄下来
包含获取perf_counter 并包含 perf_counter.h 
在存在 arm-2d 的情况下,直接包含 arm_2d.h 或者 arm_2d_utils.h
……
五、说在后面的话  
我承认 arm compiler 5 迁移到 arm compiler 6 不是一个轻松的过程,但也绝非大家想象的那样痛苦,很多时候,也许只是在 mdk 中更换一个选项那么简单:
不试一试怎么知道呢?
对主流芯片大厂,比如 st和nxp来说,它们的库早就完成了对 arm compiler 6的支持,可以说如果你遇到编译器兼容问题,应该首先考虑下载最新版本的驱动库。
本文介绍的方法,基本上可以应对常见的从arm compiler 5到 arm compiler 6可能遇到的问题。
这当然不是一份万能的解药,对于一些特殊的情况,我们将在后续文章中进行专题讨论。


紫光DDR3 4GB*2 1600内存详细评测
数字电路降噪语音捕获SoC提升语音辨识度
华为展开转型,实现旗下多个产业的协同发展
本田雅阁和斯柯达全新速派,谁的性价比更高?谁更值得拥有?
百度发布AI市场2.0 帮助合作企业发展壮大
介绍从AC5到AC6转型之路
永磁同步电机的特点 永磁同步电机和交流同步电机区别
芯动华南 智向未来 荣湃半导体2022慕尼黑华南电子展盛况回顾
国家地质实验测试中心招标,仪器行业迎商机
瑞萨电子的RL78产品系列特点,以及瑞萨电子针对中国市场的MCU产品特点
RFID实质性的应用阶段,开环应用亟待破题
维珍网络已完成400Gbps单光纤传输技术验证
VIAVI推出5G核心网仿真仪 加速网络部署及发展进程
关于Patterning的选择和性能分析以及应用
虹科分享 | CAN FD技术在机器狗中的应用
Noria智能空调 全球最小的空调
数据中心蓄电池监测系统
车载智能化UHF无源RFID读写器的特点介绍
小米10新版本入网 或将搭载高通骁龙865
云南大学采购南京大展DSC300C差示扫描量热仪