ATtiny13上__uint24数据迁移汇编的正确性与优化问询
问题背景
我原本使用的C代码如下:
volatile register uint16_t counter asm("r12"); __uint24 getCounter() { __uint24 res = counter; res = (res << 8) | TCNT0; return res; }
这是运行在ATtiny13单片机上的内联热点函数,因资源紧张需要优化。原代码编译后的汇编指令为:
getCounter: movw r24,r12 ldi r26,0 clr r22 mov r23,r24 mov r24,r25 in r25,0x32 or r22,r25 ret
我自行编写了内联汇编实现:
inline __uint24 getCounter() { //__uint24 res = counter; //res = (res << 8) | TCNT0; uint32_t result; asm( "in %A[result],0x32" "\n\t" "movw %C[result],%[counter]" "\n\t" "mov %B[result],%C[result]" "\n\t" "mov %C[result],%D[result]" "\n\t" : [result] "=r" (result) : [counter] "r" (counter) : ); return (__uint24) result; }
这里用uint32_t是为了分配第四个连续寄存器,让编译器知晓该寄存器被修改(无法在破坏列表中使用类似"%D[result]"的写法)。
我的疑问:
- 自编汇编是否正确?目前测试看似可行。
- 是否有无需复杂汇编、让编译器更好优化
getCounter()的方法? - 是否有更优的汇编实现方式?
补充说明:使用movw是为保证对counter的读取为原子操作,因counter会在中断中被递增。
解答
1. 自编汇编的正确性
你的实现逻辑是正确的:
- 先读取
TCNT0到结果的低8位(%A[result]) - 用
movw原子读取counter到%C[result]和%D[result](对应32位变量的高16位) - 通过两次
mov调整寄存器位置,最终得到的__uint24结果结构为[C:B:A],完全匹配counter<<8 | TCNT0的逻辑。
需要注意的细节:
- ATtiny13的
movw操作偶对寄存器,counter绑定在r12,所以movw %C[result],%[counter]会读取r12和r13,和uint16_t counter的定义匹配,无问题。 - 用
uint32_t分配连续4个寄存器,指令操作所有四个后只取低24位返回,逻辑自洽。
2. 无需复杂汇编的优化方法
可以通过调整C代码写法,引导编译器生成更优汇编,同时保证counter读取的原子性:
volatile register uint16_t counter asm("r12"); static inline __uint24 getCounter() { uint16_t cnt = counter; // 强制原子读取counter return ((__uint24)cnt << 8) | TCNT0; }
或者更简洁的写法:
static inline __uint24 getCounter() { return ((__uint24)counter << 8) | TCNT0; }
关键优化点:
- 声明函数为
static inline,强制编译器内联,省去ret指令的开销(ATtiny13资源有限,内联收益明显)。 - 明确
counter的读取为单个uint16_t操作,编译器会自动用movw实现原子读取,避免拆分。 - 编译器会直接生成
in指令读取TCNT0,不会产生原汇编中ldi r26,0、clr r22这类冗余指令。
3. 更优的汇编实现方式
可以直接利用AVR平台__uint24的寄存器布局(三个连续寄存器,顺序为[高:中:低]),无需借助uint32_t,大幅简化汇编:
static inline __uint24 getCounter() { __uint24 res; asm volatile( "in %A[res], 0x32" "\n\t" // 读取TCNT0到res的低8位 "movw %B[res], %[cnt]" "\n\t" // 原子读取counter到res的中8位和高8位 : [res] "=r" (res) : [cnt] "r" (counter) : ); return res; }
这个版本的优势:
- 仅用两条核心指令,比你的原汇编少两条
mov指令,效率更高。 - 直接匹配
__uint24的寄存器结构:%A[res]存TCNT0,%B[res]和%C[res]存counter的低8位和高8位,最终结构正好是counter<<8 | TCNT0。 - 加上
asm volatile防止编译器重排或删除汇编指令(因TCNT0是硬件寄存器,读取有副作用)。
内容的提问来源于stack exchange,提问作者Kryštof Vosyka
相关产品推荐
相关产品推荐

