在开始之前,建议先备份你的工程代码。
迁移条件
要使用 ARM Compiler 6,建议MDK的版本至少为:
- MDK版本5.23或更高版本
MDK版本5.23提供两个编译器,分别是 ARM Compiler 5.06 和 ARM Compiler 6.6。
软件包也需要支持 ARM Compiler 6,以下是支持 ARM Compiler 6 的最低软件包版本:
Keil MDK-Middleware包 : 版本7.4.0及以上Keil ARM Compiler Support 包 : 版本1.3.0及以上ARM CMSIS 包 : 版本5.0.1及以上
切换编译器
-
使用MDK打开工程。
-
选择 Project - Options for Target from the menu。
-
点击 Target 选项卡,找到 ARM Compiler: 下拉列表。
-
设置ARM编译器为 Version 6 。
-
点击 OK 键确认更改。
切换后的ARM Compiler 6所有设置都为默认值。
设置警告级别
ARM Compiler 6 提供的警告级别比 ARM Compiler 5 多,如果你习惯 ARM Compiler 5 的警告级别,选择
可以通过在参数前面加上
例如,通过选项

在迁移的第一步中,建议将级别切换为“无警告”。 这将使您可以专注于错误消息。
解决所有错误信息后,选择
如果想测试下代码是否规范,可以选择
设置优化级别
选择
如果想代码执行速度快,可以选择
如果想减少代码大小,可以选择
比如我的工程,使用
不兼容的语言扩展
主要是代码中的
-
替换CMSIS头文件,这里使用的是5.6.0版本的CMSIS。
如果安装了较新的Keil,可以在路径.\Keil_v5\ARM\PACK\ARM\CMSIS 目录中找到合适的CMSIS版本。
将.\Keil_v5\ARM\PACK\ARM\CMSIS\5.6.0\CMSIS\Core\Include 内的文件替换到工程code_cm3.h 所在的文件夹。 -
修改lwip协议栈的
cc.h 文件,因为lwip使用到了编译器的语言扩展,比如取消结构体的对齐优化、指定变量对齐方式等,这些语言扩展ARM Compiler 5和ARM Compiler 6并不相同。
在cc.h 文件中引用CMSIS提供的cmsis_compiler.h 文件,然后修改结构体封装与对齐宏代码为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27/* Arm Compiler 4/5 */
#if defined ( __CC_ARM )
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(fld) fld
#define ALIGNED(n) __align(n)
/* Arm Compiler above 6.10.1 (armclang) */
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __PACKED
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(fld) fld
#define ALIGNED(n) __ALIGNED(n)
/* GNU Compiler */
#elif defined ( __GNUC__ )
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(fld) fld
#define ALIGNED(n) __attribute__((aligned (n)))
#else
#error "Unsupported compiler"
#endif -
程序中使用了
__packed 、__align(n) 、__inline 、__weak 的地方分别用CMSIS提供的宏__PACKED 、__ALIGNED(n) 、__INLINE 、__WEAK 代替。注意
__packed 和__attribute__((packed)) 的使用区别:ARM Compiler 5 使用
__packed :1
2
3
4
5typedef __packed struct
{<!-- -->
char x;
int y;
} X;ARM Compiler 6 使用
__attribute__((packed)) :1
2
3
4
5typedef struct __attribute__((packed))
{<!-- -->
char x;
int y;
} X; -
如果使用ARM Compiler 5 时习惯使用
typedef __packed struct {}X; 句法,推荐改为CMSIS提供的宏句法:typedef __PACKED_STRUCT {}X; 当使用ARM Compiler 5 时会自动扩展为:
typedef __packed struct {}X; 当使用ARM Compiler 6 时会自动扩展为:
typedef struct __attribute__((packed)) {}X; -
如果使用内联函数建议按照以下格式:
1
2
3
4__STATIC_INLINE func_name(arg)
{<!-- -->
//函数体
}防止ARM Compiler 6在
-O0 、-O1 级别设置下,链接时出现未定义符号的错误。(在这种优化级别下__INLINE 可能并不会内联)
不兼容的语言扩展总结如下:
| ARM Compiler 5 | ARM Compiler 6 | CMSIS(推荐) |
|---|---|---|
| __align(x) | __attribute__((aligned(x))) | __ALIGNED(x) |
| __alignof__ | __alignof__ | |
| __ALIGNOF__ | __alignof__ | |
| __asm | 见汇编迁移 | __ASM |
| __const | __attribute__((const)) | |
| __forceinline | __attribute__((always_inline)) | |
| __global_reg | 不支持 | |
| __inline | __inline__ 此功能使用取决于语言模式 | __INLINE __STATIC_INLINE |
| __int64 | 没有对等选项.使用 |
int64_t |
| __irq | __attribute__((interrupt)) | Cortex-M ISR不需要此关键字 |
| __packed __packed x struct |
__attribute__((packed)) struct x attribute((packed)) |
__PACKED __PACKED_STRUCT x |
| __pure | __attribute__((const)) | |
| __smc | 不支持,使用内联汇编或等效程序 | |
| __softfp | __attribute__((pcs(“aapcs”))) | |
| __svc | 不支持,使用内联汇编或等效程序 | |
| __svc_indirect | 不支持,使用内联汇编或等效程序 | |
| __thread | __thread | |
| __value_in_regs | __attribute__((value_in_regs)) | |
| __weak | __attribute__((weak)) | __WEAK |
| __writeonly | 不支持 |
提示:可以使用uVision IDE的查找功能来查找上述关键字,然后做迁移处理。
内嵌汇编
ARM Compiler 6 完全改变了处理汇编代码的策略。
汇编语法现在兼容GNU风格而不是ARM风格。 汇编也是由C编译器完成, 无需单独的汇编器。
-
FreeRTOS的移植层由
..\FreeRTOS\Source\portable\RVDS\ARM_CM3 目录下的port.c 和portmacro.h 文件改为..\FreeRTOS\Source\portable\GCC\ARM_CM3 目录下的port.c 和portmacro.h 文件。这是因为这两个文件会涉及内嵌汇编。
-
自定义的内嵌汇编函数。
ARM Compiler 5:
1
2
3
4
5
6
7__asm uint32_t __get_flash_base(void)
{<!-- -->
IMPORT |Image$$ER_IROM1$$RO$$Base|;
ldr r0,=|Image$$ER_IROM1$$RO$$Base|;
bx lr;
}ARM Compiler 6:(看了下帮助手册,也可以不使用汇编)
1
2
3
4
5
6uint32_t get_flash_base(void)
{<!-- -->
extern uint32_t Image$$ER_IROM1$$RO$$Base;
return &Image$$ER_IROM1$$RO$$Base;
}
语法更严格
-
比如某个函数之前要对外开放,.c和.h中定义和声明都相同。后来在.c文件中将该函数定义为本地函数,使用static修饰,但.h中忘记删除也没有做相应修改。这种情况下,ARM Compiler 5 不会报错,ARM Compiler 6 会报错:
../file_name.c(10): error: static declaration of 'func_name' follows non-static declaration -
如下代码:
1for(i=0; i<0x7E-0x20; i++)ARM Compiler 5 不会报错,ARM Compiler 6 会报错:
../file_name.c(332): error: invalid suffix '-0x20' on integer constant 需要将代码改为:
1for(i=0; i<0x7E - 0x20; i++)
优化问题
以下代码在 ARM Compiler 5 中,正常执行,但在 ARM Compiler 6 中,只要不是
1 2 3 4 5 | void delay_us (uint32_t ul_time) {<!-- --> ul_time *= 30; while(--ul_time != 0); } |
所以当升级到 ARM Compiler 6 出现软件IIC出错、软件SPI出错等问题,可以检查是否有类似的代码。
需要改成:
1 2 3 4 5 6 | void delay_us (uint32_t ul_time) {<!-- --> ul_time *= 30; while(--ul_time != 0) __nop(); } |
Keil编译器保证
编译时间和大小
-
-O0:Program Size: Code=200360 RO-data=20576 RW-data=96 ZI-data=76316
Build Time Elapsed: 00:00:25 -
-O1:Program Size: Code=119328 RO-data=16824 RW-data=96 ZI-data=76300
Build Time Elapsed: 00:00:25 -
-O2:Program Size: Code=153340 RO-data=17100 RW-data=96 ZI-data=76300
Build Time Elapsed: 00:00:26 -
-O3:Program Size: Code=162292 RO-data=17040 RW-data=96 ZI-data=76308
Build Time Elapsed: 00:00:27 -
-Ofast:Program Size: Code=161896 RO-data=17040 RW-data=96 ZI-data=76308
Build Time Elapsed: 00:00:26 -
-Os balanced:Program Size: Code=115628 RO-data=17048 RW-data=96 ZI-data=76300
Build Time Elapsed: 00:00:28 -
-Oz image size:Program Size: Code=103784 RO-data=17020 RW-data=96 ZI-data=76308
Build Time Elapsed: 00:00:25 -
-Oz image size LTO:Program Size: Code=85888 RO-data=17064 RW-data=40 ZI-data=75960
Build Time Elapsed: 00:00:32
与Compiler 5 对比(我的程序-可能不具有一般性):
- -O2:Program Size: Code=94232 RO-data=16736 RW-data=540 ZI-data=75640
Build Time Elapsed: 00:00:16
参考文档
- 《Arm? Compiler Migration and Compatibility Guide》(内容详细)
- 《Migrate ARM Compiler 5 to ARM Compiler 6》 (AN298)