arm v8r中__asm__的用法
在 ARMv8-R 架构中,__asm__
是 GCC 编译器提供的内联汇编语法,用于在 C/C++ 代码中直接嵌入汇编指令,实现对底层硬件的精确控制(如协处理器操作、内存屏障、实时任务优化)。以下是其详细用法和关键注意事项:
🔧 一、基础语法结构
__asm__ [volatile] (
"汇编指令模板"
: 输出操作数列表
: 输入操作数列表
: 破坏寄存器列表
);
volatile
:可选,禁止编译器优化内联汇编(实时系统必加)。- 汇编指令模板:包含汇编指令和占位符(如
%0
,%1
)。 - 输出操作数列表:C 变量接收汇编结果(格式:
"=约束"(变量)
)。 - 输入操作数列表:C 变量传入汇编(格式:
"约束"(变量)
)。 - 破坏寄存器列表:声明被汇编修改的寄存器(如
"r0", "memory"
)。
⚙️ 二、关键组件详解
1. 操作数约束(Constraints)
约束符 | 含义 | 示例 |
---|---|---|
r |
通用寄存器 | "r" (val) |
l |
立即数 | "l" (0x100) |
m |
内存地址 | "m" (*ptr) |
=&r |
早期破坏寄存器(输出专用) | "=&r" (out) |
I |
特定范围的立即数 | "I" (0xFF) |
2. 占位符格式
%0
,%1
... 按操作数顺序引用(输出在前,输入在后)。- 可命名占位符:
%[name]
(需在操作数列表中声明,如[name] "r"(var)
)。
🛠️ 三、ARMv8-R 典型应用场景
1. 配置 MPU 区域
void set_mpu_region(uint32_t base, uint32_t attr) {
__asm__ volatile (
"MCR p15, 0, %0, c6, c0, 0 \n" // 写 MPU_RBAR
"MCR p15, 0, %1, c6, c1, 4 \n" // 写 MPU_RASR
: /* 无输出 */
: "r" (base), "r" (attr) // 输入:base→%0, attr→%1
: "memory" // 破坏内存状态
);
}
2. 启用 TCM 并设置基址
void enable_itcm(uint32_t addr) {
uint32_t ctrl_reg;
// 1. 读取 SCTLR
__asm__ volatile (
"MRC p15, 0, %0, c1, c0, 0 \n" // 读 SCTLR → ctrl_reg
: "=r" (ctrl_reg)
:
:
);
// 2. 设置 ITCM 基址
__asm__ volatile (
"MCR p15, 0, %0, c9, c1, 0 \n" // 写 ITCM 基址
:
: "r" (addr)
: "memory"
);
// 3. 使能 ITCM(设置 SCTLR.ITCMEN)
ctrl_reg |= (1 << 18); // ITCMEN = bit18
__asm__ volatile (
"MCR p15, 0, %0, c1, c0, 0 \n" // 写回 SCTLR
:
: "r" (ctrl_reg)
: "memory"
);
}
3. 数据内存屏障(DMB)
void data_memory_barrier() {
__asm__ volatile (
"DMB SY \n" // 确保所有内存访问完成
::
: "memory"
);
}
⚠️ 四、关键注意事项
1. 寄存器与内存破坏声明
- 显式声明破坏:若汇编修改了寄存器或内存,必须在破坏列表中声明:
__asm__ volatile ( "MOV r0, #1 \n" "ADD %0, r0, %0 \n" : "+r" (val) // 输入输出操作数 : : "r0" // 声明 r0 被破坏 );
"memory"
:声明汇编可能修改任意内存(强制刷新寄存器缓存值)。
2. 特权指令限制
- ARMv8-R 的协处理器指令(如
MCR
/MRC
)必须在特权模式执行。 - 用户模式调用需通过 SVC 异常切换到特权模式。
3. 实时性优化
- 避免阻塞操作:慎用
CP15
缓存维护指令(如DC ISW
),可能阻塞流水线。 - 短代码原则:内联汇编尽量简短,避免影响中断延迟。
💡 五、高级技巧:混合命名与顺序占位符
uint32_t read_sctlr() {
uint32_t val;
__asm__ volatile (
"MRC p15, 0, %[out], c1, c0, 0 \n"
: [out] "=r" (val) // 命名占位符 %[out]
:
:
);
return val;
}
✅ 六、错误示例分析
错误:未声明破坏寄存器
// 错误!未声明 r0 被修改
__asm__ ("MOV r0, #1 \n ADD %0, r0, %0 \n" : "+r" (val));
后果:编译器可能已用 r0
存储其他值,导致数据损坏。
修正:
__asm__ volatile (
"MOV r0, #1 \n"
"ADD %0, r0, %0 \n"
: "+r" (val)
:
: "r0" // 声明 r0 被破坏
);
💎 总结
在 ARMv8-R 中使用 __asm__
的核心要点:
- 语法规范:严格遵循
指令模板 : 输出 : 输入 : 破坏列表
结构。 - 实时安全:
- 必加
volatile
禁止优化。 - 特权指令需在特权模式下运行。
- 必加
- 资源管理:
- 显式声明破坏的寄存器(如
"r0"
)或内存("memory"
)。 - 避免在中断上下文中执行长耗时操作(如缓存维护)。
- 显式声明破坏的寄存器(如
- 应用场景:MPU/TCM 配置、内存屏障、协处理器操作(CP15)、低延迟中断处理。
📌 建议:对性能要求极高的代码(如中断服务程序),可将完整汇编函数放在
.S
文件中,通过 TCM 链接确保确定性执行。