gcc 在嵌入式软件中一些常见的用法(待补充)
大多数情况,嵌入式软件工程师并不需要知道gcc编译器是工作的,但是随着调试深入,了解gcc在软件中的一些常见的用法大有裨益。
这里列一下遇到的一些嵌入式软件中用到的一些gcc知识。
1 GCC关闭某函数的优化
软件库使用O2/O3优化,有时debug时,需要关闭其中某些函数的优化(即使用此函数使用O0优化),可以采用如下方式:
注意:只能放到函数外,不能放到函数内部。在特定代码前保存当前的编译选项,然后对特定的代码使用O0优化级别,最后再恢复之前保存的编译选项。
示例如下:
#pragma GCC push_options
#pragma GCC optimize("O0")
void conv_test()
{
...
}
#pragma GCC pop_options
2 怎么声明某个数组多少字节对齐?
为了效率,CPU从内存取数据并不是一字节一字节去读的,而是一次读取多个字节(如4字节),如果变量地址不对齐(不是4的倍数),则会降低CPU效率。
对齐的含义:
2字节对齐:地址的二进制最后1位为0(2^1)
4字节对齐:地址的二进制最后2位为0(2^2)
8字节对齐:地址的最后3位为0(2^3)
16字节对齐:地址的最后4位为0(2^4)
32字节对齐:地址的最后5位为0(2^5)
64字节对齐:地址的最后6位为0(2^6)
如:声明某个数组16Byte对齐:
int32_t __attribute__((aligned(16))) a_array[1024];
这样,编译后a_array的地址能被16整除(地址低4bit全为0,2^4 = 16)
3 怎么将某数组定义到外部flash或ram上?
有时数组太大,直接放到片内ram放不下,需要放到外部SDRAM中,可以使用如下方法:
方法1:__ attribute __( at(绝对地址) )
例如:
const uint8_t bigArray[] __attribute__((at(0x30000000)));
注意:有可能 GNU GCC 不再支持at方法,方法1不行时,请采用方法2。
方法2:__ attribute __((section(“块名”)))
这种方法稍显麻烦,需要修改链接脚本。
例如:
修改C代码
const uint8_t bigArray[] __attribute__((section(".bigArray")));
// 也可以同时设置段于对齐方式
const uint8_t bigArray[] __attribute__((section(".bigArray"))) __attribute__((aligned(64)));
修改lds链接文件,假设外部SDRAM起始地址0x30000000,大小为30MB
/* Program Entry, set to mark it as "used" and avoid gc */
MEMORY
{
ROM (rx) : ORIGIN =0x08000000,LENGTH =128k
RAM (rw) : ORIGIN =0x20000000,LENGTH =512k
EXTSDRAM (rw) : ORIGIN =0x30000000,LENGTH =32M /* 新增 */
}
/* 在SECTIONS里新增段,段名要与C代码中的字段一致 */
.bigArray (NOLOAD) : ALIGN(4)
{
. = ALIGN(4);
*(.bigArray)
*(.bigArray.*)
. = ALIGN(4);
__bigArray_free__ = .;
} > EXTSDRAM
4 GCC关闭函数的自动内敛
如函数:
static void foo() {}
希望编译时不要内涵这个函数,可以改为如下写法:
void __attribute__ ((noinline)) foo()
{
...
}
参考: