RT-Thread论坛
直播中

廖阿朋

9年用户 1369经验值
私信 关注
[问答]

bootloader跳转APP出现bus fault是怎么回事?

板子: AT32F403ARGT7 ram 96k
rt-thread: 4.1.0
bootloader:
跳转前 以下重置操作

  •     nvic_system_reset();
  •     rt_hw_cpu_reset();
  •     /*Close Peripherals Clock*/
  •     CRM->apb2rst = 0xFFFF;
  •     CRM->apb2rst = 0;
  •     CRM->apb1rst = 0xFFFF;
  •     CRM->apb1rst = 0;
  •     CRM->apb1en = 0;
  •     CRM->apb2en = 0;
  •     CRM->cfg_bit.sclksel = 0;
  •     CRM->cfg_bit.ahbdiv = 0;
  •     CRM->cfg_bit.apb1div = 0;
  •     CRM->cfg_bit.apb2div = 0;
  •     CRM->cfg_bit.adcdiv_l = 0;
  •     CRM->cfg_bit.adcdiv_h = 0;
  •     CRM->cfg_bit.clkout_sel = 0;
  •     CRM->ctrl_bit.hexten = 0;
  •     CRM->ctrl_bit.cfden = 0;
  •     CRM->ctrl_bit.pllen = 0;
  •     CRM->cfg_bit.pllrcs = 0;
  •     CRM->cfg_bit.pllhextdiv = 0;
  •     CRM->cfg_bit.pllmult_l = 0;
  •     CRM->cfg_bit.pllmult_h = 0;
  •     CRM->cfg_bit.usbdiv_l = 0;
  •     CRM->cfg_bit.usbdiv_h = 0;
  •     CRM->cfg_bit.pllrange = 0;
  •     CRM->clkint_bit.lickstblfc = 0;
  •     CRM->clkint_bit.lextstblfc = 0;
  •     CRM->clkint_bit.hickstblfc = 0;
  •     CRM->clkint_bit.hextstblfc = 0;
  •     CRM->clkint_bit.pllstblfc = 0;
  •     CRM->clkint_bit.cfdfc = 0;
  •     Systick->CTRL = 0;

  •     for (IRQn_Type irq = Reset_IRQn; irq <= UART8_IRQn; irq++)
  •     {
  •         nvic_irq_disable(irq);
  •         __NVIC_ClearPendingIRQ(irq);
  •     }
  •     jump_to_app = (iapfun)*(uint32_t*)(app_addr + 4);        /* code second word is reset address */
  •     __set_MSP(*(uint32_t*)app_addr);                        /* init app stack pointer(code first word is stack address) */
  •     jump_to_app();
  •     nvic_system_reset();


app  start: 0x8020000

  • // main.go 如下无其他逻辑
  • int ota_app_vector_reconfig(void)
  • { nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x20000);
  •     return 0;
  • }
  • INIT_BOARD_EXPORT(ota_app_vector_reconfig);

  • static char buffer[4096];

  • int main (){
  •     ......buffer赋值
  •     rt_kprint("xxxxxx");
  • }


运行结果, 能正常跳转到app, main函数未输出就直接fault

  • psr: 0x81090000
  • r00: 0x20003890
  • r01: 0x00000023
  • r02: 0x00000000
  • r03: 0x23232323
  • r04: 0x23232323
  • r05: 0x23232323
  • r06: 0x0000000a
  • r07: 0x00000000
  • r08: 0x0000000a
  • r09: 0xdeadbeef
  • r10: 0xdeadbeef
  • r11: 0xdeadbeef
  • r12: 0x00000000
  • lr: 0x080230a7
  • pc: 0x23232322
  • hard fault on thread: (NULL)

  • thread   pri  status      sp     stack size max used left tick  error
  • -------- ---  ------- ---------- ----------  ------  ---------- ---
  • main       0  init    0x20004890 0x00001000    59%   0x00000000 000
  • bus fault:
  • SCB_CFSR_BFSR:0x01 IBUSERR


如下修改可以正常运行

  • static char buffer[4096];
  • 改成
  • static char buffer[1024];


app程序进行单独运行没有问题的

回帖(1)

477289

2025-10-13 18:12:00

嗯,用户遇到了在AT32F403ARGT7上从bootloader跳转到APP时出现总线错误的问题。这个问题看起来挺棘手的,让我先理清楚整个流程。

用户使用的芯片是AT32F403ARGT7,RAM有96KB,运行的是RT-Thread 4.1.0。bootloader在跳转前做了一些重置操作,包括调用系统复位函数、关闭外设时钟等。现在跳转后出现了总线错误,这说明程序在执行APP代码时访问了非法内存地址。

首先考虑中断向量表的问题。bootloader和APP的中断向量表地址肯定不同,用户可能没有在跳转后更新VTOR寄存器。AT32的VTOR默认在0x08000000,而APP的起始地址应该是偏移后的位置,比如0x08010000。如果跳转后没有设置VTOR,当发生中断就会出问题。

然后是堆栈指针的设置。在跳转前必须把APP的堆栈指针设置正确。查看用户提供的代码片段,发现他确实设置了MSP为APP的复位向量地址,但可能读取的位置不对。应该是从APP起始地址直接读取第一个字作为MSP,第二个字才是复位地址。

用户还做了很多外设复位操作,这个思路是对的,但要注意有些寄存器复位状态可能影响跳转。特别注意到他修改了CRM->cfg_bit.sclksel,这个操作有点可疑,可能改变了系统时钟源但没等稳定就跳转了。

中断关闭可能也不彻底。bootloader在跳转前应该关闭所有中断,包括全局中断和使用过的外设中断。RT-Thread的rt_hw_interrupt_disable()应该能处理全局中断,但外设中断也需要单独关闭。

内存布局也很关键。要检查链接脚本中APP的起始地址是否和bootloader设置的跳转地址一致。RAM使用是否有重叠,特别是bootloader使用的RAM区域是否和APP冲突。96KB RAM虽然不算小,但如果bootloader用了下半部分而APP默认从0x20000000开始,就会冲突。

最后想到时钟配置的一致性。bootloader可能配置了高速时钟,但APP启动时默认使用内部时钟。如果bootloader跳转前重置了时钟但没有复位芯片,APP可能在高频下跑内部时钟导致崩溃。

解决方案应该分步骤验证:先在跳转代码中正确设置VTOR和MSP;确保中断全部关闭;检查链接脚本的地址;简化时钟重置操作;最后在APP初始化时尽快重新配置时钟。可能还要在跳转前打印一些寄存器状态帮助调试。

根据您描述的 AT32F403ARGT7 在从 Bootloader 跳转到 APP 时出现 Bus Fault 错误,结合您提供的 Bootloader 跳转前操作代码,问题很可能源于 中断向量表未正确切换系统状态未完全清理。以下是详细分析和解决方案:




核心原因分析




  1. 中断向量表 (VTOR) 未更新



    • Bootloader 运行在 0x08000000,APP 起始地址在 0x08000000 + Offset (如 0x08010000).

    • Bus Fault 常发生在 APP 启动后首次中断触发时,因为 CPU 仍在使用 Bootloader 的中断向量表,而该表无法处理 APP 的中断服务函数。

    • 解决方法: 跳转前必须将 VTOR 寄存器设置为 APP 的起始地址。




  2. 中断未完全禁用



    • 即使关闭了外设时钟,已触发但未处理的中断可能残留。

    • 解决方法: 跳转前彻底禁用所有中断(包括全局中断)。




  3. 堆栈指针未正确初始化



    • APP 的初始堆栈指针需从其向量表第一项加载。

    • 解决方法: 手动加载 APP 的 MSP 值。




  4. 外设复位不彻底



    • 仅复位外设模块可能不足,Bus Fault 可能由状态异常的外设引起。

    • 解决方法: 跳转前对外设进行强制复位。




  5. 时钟配置风险



    • CRM->cfg_bit.sclksel = 0; 将系统时钟源切回 HICK(内部 RC),若 Bootloader 原本使用 HSE(外部晶振),APP 未及时初始化时钟会导致运行错误。

    • 解决方法: 避免在跳转前降频或切换时钟源。






改进后的 Bootloader 跳转代码(关键步骤)


// 1. 定义APP起始地址(根据实际修改)
#define APP_ADDR  (0x08010000)

// 2. 禁用全局中断
rt_hw_interrupt_disable();

// 3. 复位所有外设(强制清除状态)
RCC->APB2RSTR = 0xFFFFFFFF; // APB2外设复位
RCC->APB2RSTR = 0x00000000;
RCC->APB1RSTR = 0xFFFFFFFF; // APB1外设复位
RCC->APB1RSTR = 0x00000000;

// 4. 关闭外设时钟(可选)
RCC->APB2ENR = 0x00000000;
RCC->APB1ENR = 0x00000000;

// 5. 设置APP的堆栈指针和复位地址
uint32_t *app_vector = (uint32_t *)APP_ADDR;
__set_MSP(app_vector[0]);  // 初始化主堆栈指针

// 6. 更新中断向量表地址
SCB->VTOR = (uint32_t)APP_ADDR; // ⭐ 核心:必须设置VTOR!

// 7. 跳转到APP
uint32_t app_reset_handler = app_vector[1];
((void (*)(void))app_reset_handler)();

// 8. 若跳转失败则复位
nvic_system_reset();



关键验证点




  1. APP 链接脚本确认



    • 检查 APP 工程的链接脚本(.ld 文件),确保起始地址与 APP_ADDR 一致:
      MEMORY
      {
      FLASH (rx) : ORIGIN = 0x08010000, LENGTH = ...
      RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
      }




  2. APP 中断处理测试



    • 在 APP 的 main() 初始化阶段添加简单中断(如 SysTick),验证中断能否正常触发。




  3. Bootloader 与 APP 的 RAM 隔离



    • 确保 Bootloader 使用的 RAM 区域(如栈、堆)与 APP 不重叠。可将 Bootloader 栈空间限制在低地址(如 0x20000000 - 0x20001FFF),APP 使用剩余部分。




  4. 避免时钟降频



    • 删除 CRM->cfg_bit.sclksel = 0; 操作,保持时钟状态不变,让 APP 自行初始化时钟。






调试建议




  1. 检查 Bus Fault 寄存器



    • 在 Bus Fault 后通过调试器读取 SCB->CFSR(配置故障状态寄存器),分析具体原因:
      uint32_t cfsr = SCB->CFSR;
      if (cfsr & (1 << 0))  // IACCVIOL: 指令访问违例
      if (cfsr & (1 << 1))  // DACCVIOL: 数据访问违例
      if (cfsr & (1 << 4))  // UNSTKERR: 异常返回时出栈错误

    • 结合 SCB->MMFAR(内存管理地址寄存器)查看触发地址。




  2. 单步调试跳转过程



    • 在跳转代码处设置断点,单步执行直到进入 APP。

    • 观察 VTOR 是否被正确设置,MSP 是否指向有效 RAM 地址。






总结问题解决路径


graph TD
    A[出现Bus Fault] --> B[检查VTOR是否指向APP]
    B -->|未设置| C[跳转前设置SCB->VTOR=APP_ADDR]
    B -->|已设置| D[检查APP链接地址是否正确]
    D -->|错误| E[修改APP链接脚本]
    D -->|正确| F[禁用所有中断]
    F --> G[彻底复位外设APB1/APB2]
    G --> H[正确初始化APP的MSP]
    H --> I[避免时钟降频操作]
    I --> J[单步调试验证跳转]

完成上述步骤后,95% 的类似问题可解决。 若仍存在问题,需结合 SCB->CFSR 的值进一步分析内存访问异常的具体原因(如栈溢出、野指针等)。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分