You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cortex-M0+下从Thumb内联汇编分支到C符号的问题

解决Cortex-M0+中bl指令被转为blx导致HardFault的问题

这个问题我之前在做M0+的RAM动态代码生成时也碰到过,核心原因和Cortex-M的模式识别规则有关,咱们一步步来解决:

为什么会生成blx指令?

Cortex-M系列用地址的**最低位(LSB)**来区分指令模式:

  • LSB=1:目标是Thumb模式代码(M0+唯一支持的模式)
  • LSB=0:目标是ARM模式代码

你的code_buf是uint16_t数组,RAM里的数组地址是对齐的(偶数),所以LSB为0。编译器看到这个地址时,误以为要跳转到ARM模式代码,所以自动生成blx指令——而M0+根本不支持ARM模式,直接触发HardFault。

强制编译器生成bl指令的方法

方法1:将数组地址转为Thumb模式函数指针

先定义一个Thumb函数指针类型,然后把code_buf的地址置位LSB(与1做或操作),明确告诉编译器这是Thumb代码的入口:

// 定义Thumb函数指针类型
typedef void (*thumb_func_t)(void);

// 把数组地址转为Thumb模式函数指针(置位LSB)
thumb_func_t ram_code_entry = (thumb_func_t)((uintptr_t)code_buf | 1);

void jump() {
    register volatile uint32_t* PASET asm("r0") = &(PA->OUTSET.reg);
    register volatile uint32_t* PACLR asm("r1") = &(PA->OUTCLR.reg);
    register uint32_t set asm("r2") = startset;
    register uint32_t cl0 asm("r3") = clears[0];
    register uint32_t cl1 asm("r4") = clears[1];
    register uint32_t cl2 asm("r5") = clears[2];
    register uint32_t cl3 asm("r6") = clears[3];
    register uint32_t dl0 asm("r8") = delays[0];
    register uint32_t dl1 asm("r9") = delays[1];
    register uint32_t dl2 asm("r10") = delays[2];
    register uint32_t dl3 asm("r11") = delays[3];
    
    asm volatile ( "bl %0\n"
        : [set]"+r" (set)
        : [target]"r" (ram_code_entry), [PASET]"r" (PASET), [PACLR]"r" (PACLR), 
          [cl0]"r" (cl0), [cl1]"r" (cl1), [cl2]"r" (cl2), [cl3]"r" (cl3), 
          [dl0]"r" (dl0), [dl1]"r" (dl1), [dl2]"r" (dl2), [dl3]"r" (dl3)
        : "lr" );
}

方法2:直接在汇编参数中处理地址

如果不想额外定义函数指针,也可以直接在汇编的输入参数里把地址的LSB置1,效果是一样的:

void jump() {
    register volatile uint32_t* PASET asm("r0") = &(PA->OUTSET.reg);
    register volatile uint32_t* PACLR asm("r1") = &(PA->OUTCLR.reg);
    register uint32_t set asm("r2") = startset;
    register uint32_t cl0 asm("r3") = clears[0];
    register uint32_t cl1 asm("r4") = clears[1];
    register uint32_t cl2 asm("r5") = clears[2];
    register uint32_t cl3 asm("r6") = clears[3];
    register uint32_t dl0 asm("r8") = delays[0];
    register uint32_t dl1 asm("r9") = delays[1];
    register uint32_t dl2 asm("r10") = delays[2];
    register uint32_t dl3 asm("r11") = delays[3];
    
    asm volatile ( "bl %0\n"
        : [set]"+r" (set)
        : [target]"r" ((uintptr_t)code_buf | 1), [PASET]"r" (PASET), [PACLR]"r" (PACLR), 
          [cl0]"r" (cl0), [cl1]"r" (cl1), [cl2]"r" (cl2), [cl3]"r" (cl3), 
          [dl0]"r" (dl0), [dl1]"r" (dl1), [dl2]"r" (dl2), [dl3]"r" (dl3)
        : "lr" );
}

额外注意事项

  • 确保你的RAM区域在链接脚本中被标记为可执行(RX权限),否则即使指令正确,也会触发内存访问错误。
  • 动态生成的Thumb代码必须是16位对齐的(uint16_t数组天然满足这一点),否则执行时也会出问题。

内容的提问来源于stack exchange,提问作者Alberto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:36:42