THA6510 A/B 双 Bank 刷写故障分析:Swap 地址重映射与 NVM 寄存器块号错位
本文最后更新于17 天前,其中的信息可能已经过时,如有错误请留言评论。

背景

最近在给 PD305 项目(THA6510,ARC HS 五核,10MB PFlash)开发 A/B 双 Bank 备份刷写 + CAN 强刷方案。这套方案依赖芯片的硬件 Swap:低 8MB PFlash 分成两个 4MB Bank,SSW 上电时根据 DFlash 里的 UCB Swap 记录,把激活 Bank 重映射到逻辑 0x80000000 窗口,APP 永远按 0x80000000 基址链接,Boot 代码放在交换窗口之外(0x80800000+),两个 Bank 共用一份。

刷写侧的设计是:诊断仪下发的下载地址永远是链接地址,Bootloader 在擦写时加一个偏移,把数据落到非激活 Bank,刷完写有效标志、更新 Swap 记录、复位,硬件按新记录重映射,完成切换。

开发完成后,出现了一个非常规律的现象。

现象:第一次永远正常,第二次必挂

调试器刷入代码后:

  1. 第一次 CAN 刷写完全正常——擦除、烧写、CRC 校验、写有效标志、切区,一气呵成;
  2. 复位后 ECU 跑在新 Bank 上,诊断响应一切正常;
  3. 第二次 CAN 刷写,必挂在擦除命令(31 01 FF 00)上——擦除例程发出后,ECU 约 52 秒无任何响应,然后疑似复位,回到 APP。

抓 trace 分析,有两个关键细节:

  • 擦除挂起期间,ECU 对 90 01 的响应只要 0.3ms(主循环空闲),但 10 02 要 734ms 才回 50 02——这是 APP 收到编程会话请求后复位进 Boot 的耗时,证明挂起后跑的是 APP,不是 Boot;
  • 复位后旧 APP 完好无损,说明激活 Bank 一个字节都没被擦到。

也就是说:擦除例程执行过程中 ECU 异常复位了,但擦除实际上没有生效。

排查链:两个被证伪的假设

假设一:刷写偏移错了,擦到了激活 Bank(被证伪)

最初判断:映射 B 激活后,代码的偏移逻辑是"跑 B 时 offset=0",擦除地址直接打在逻辑 0x80000000——也就是激活 Bank 上,可能被硬件保护机制阻挡导致复位。

于是把偏移改成:Swap 使能时 offset 恒为 +0x400000。理由是无论映射 A 还是 B,非激活 Bank 在逻辑地址空间恒位于 +0x400000(映射 B 时逻辑 0x80400000 指向物理 Bank A)。

实测:依然挂,现象一模一样。

假设二:BS=0 流控移植引入的回归(被证伪)

第二次怀疑对象是前一天刚移植的 BS=0 优化——0x36 数据传输改成"一次流控收完整条报文、收齐再烧",流控帧从每 9 帧一个降到整条报文一个。改动量大,嫌疑也大。

把 BS=0 逻辑完整回退到原来的"每收满一个 BURST 烧一次、烧完发流控帧"(幸好移植前留了备份,按 diff 做了 13 处精准回退,保留了 31 03 切区等新功能)。

实测:依然挂。

两个假设都被证伪后,结论反而清晰了:问题与诊断接收路径无关,就是切区本身导致的——只要 ECU 跑在映射 B 状态下,擦除操作本身就必挂,跟擦哪个 Bank、用什么流控方式都没有关系。

根因:NVM 寄存器块号没跟着重映射

重新梳理硬件 Swap 的地址模型,问题浮出水面:

映射 A(恒等映射):
  逻辑 0x80000000-0x803FFFFF → 物理 Bank A(块 0/1)
  逻辑 0x80400000-0x807FFFFF → 物理 Bank B(块 2/3)

映射 B(两个 4MB 半区互换):
  逻辑 0x80000000-0x803FFFFF → 物理 Bank B(块 2/3)
  逻辑 0x80400000-0x807FFFFF → 物理 Bank A(块 0/1)

THA6510 的 NVM 控制器有 5 组 PFlash 控制寄存器(0xF00A0000 起,每 2MB 物理块一组),寄存器组按物理块编号,不参与 Swap 重映射。而擦除的触发方式是:先往块 N 的寄存器写擦除命令序列(ETYP + SDP 解锁),再往目标扇区地址写一个触发值,由地址译码决定擦哪个物理块——这个触发写会经过 Swap 重映射。

再看代码里的块号查找函数 GetBlockIndex():它用逻辑地址在静态块表(0x80000000、0x80200000……)里查出块号,然后用 PFLASH0_BASE + 块号 × 寄存器组大小 选寄存器组。

映射 A 下,逻辑地址 = 物理地址,块号永远正确——所以第一次刷写永远正常。

映射 B 下,两者必然错开。以 offset 恒 +0x400000 为例:擦除逻辑地址 0x80400000,GetBlockIndex 查出块 2,命令序列发给块 2 的寄存器;但触发写经硬件重映射后落在物理块 0(Bank A)。命令发给块 2、触发落在块 0,NVM 状态机直接错乱——挂死、等不到完成标志、最终复位。这完美解释了"擦除没生效、APP 完好、ECU 复位"的全部现象。

之前两种 offset 改法都失败也是同一个原因:问题从来不在"擦哪个 Bank",而在寄存器块号与物理目标块对不上。

修复:两处必须成对修改

修复 1:GetBlockIndex 按物理地址查块号

static uint8 Bootloader_FlashPFlashGetBlockIndex(uint32 addrUI)
{
    uint8 idxBlockUB = 0;
    Bootloader_Fls_infoBlockCfgS_TYPE *addrBlockCfgPS = NULL;
    uint32 addrPhyUI = addrUI;

#if BOOTLOADER_CFG_FLASH_SWAP_ENABLE == STD_ON
    /* Swap映射B激活时,低8MB窗口的两个4MB半区被硬件互换,NVM控制寄存器组
       按物理块编号,必须先把逻辑地址换算成物理地址再查块号 */
    if ((1U == Bootloader_bSwapEnUB) &&
        (BOOTLOADER_SWAP_INDEX_BANK_B == Bootloader_idxSwapBankUB) &&
        (addrUI >= BOOTLOADER_CFG_FLASH_ADDR_PFLASH_BEGIN) &&
        (addrUI < (BOOTLOADER_CFG_FLASH_ADDR_PFLASH_BEGIN + (2UL*BOOTLOADER_CFG_FLASH_ADDR_OFFSET_SWAP))))
    {
        addrPhyUI = addrUI ^ BOOTLOADER_CFG_FLASH_ADDR_OFFSET_SWAP;
    }
#endif

    for (idxBlockUB = 0; idxBlockUB < Bootloader_Fls_infoCfgS.numPFlashBlockUB; idxBlockUB++)
    {
        addrBlockCfgPS = ...;
        if ((addrPhyUI >= addrBlockCfgPS->addrBeginUI) &&
            (addrPhyUI < (addrBlockCfgPS->addrBeginUI + addrBlockCfgPS->numSizeUI)))
        {
            break;
        }
    }
    return idxBlockUB;
}

擦除、页写入、突发写入共 4 个调用点全部经过这个函数,一处修改全覆盖。映射 A 和 Swap 未使能时不做换算,行为与原来完全一致。

修复 2:刷写偏移恒为 +0x400000

if (Bootloader_SwapRead())
{
    /* 无论当前映射A还是B,非激活Bank在逻辑地址空间恒位于+0x400000,
       因此偏移恒为0x400000,始终刷写非激活Bank */
    Bootloader_numSwapOffsetUI = BOOTLOADER_CFG_FLASH_ADDR_OFFSET_SWAP;
}

为什么必须成对? 只改修复 1 不改修复 2(保留 P560 的"跑 B 时 offset=0"):映射 B 下擦除逻辑 0x80000000,换算后块号、触发写都指向物理块 2——这次倒是"对齐"了,但物理块 2 是正在运行的激活 Bank,会把好端端的 APP 擦掉。只改修复 2 不改修复 1,就是本次排查的故障现象。两个修改合起来才闭环。

验证

修复后重新编译、调试器刷入,连续完成 A→B→A 多轮 CAN 刷写,擦除、烧写、CRC 校验、写标志、切区全部正常。之后再把 BS=0 优化叠加回来,多次刷写依然稳定。

总结

  1. 硬件 Swap 方案的通用坑:凡是"地址重映射 + 按块编号的 Flash 控制寄存器"的架构(THA6510、AURIX UCB Swap 同类),擦写路径上所有"由地址推导资源编号"的逻辑,都必须明确用的是逻辑地址还是物理地址。这类 bug 在映射 A(恒等映射)下完全隐形,只有切到 B 才暴露——而很多项目的 A/B 功能恰恰很少真正切到 B 测过。
  2. 实现逻辑:原实现逻辑里"跑 B 时 offset=0"与 CRC 读取固定 +0x400000 的不对称,当时就存疑但没验证,最终还是不得不验证映射 B 下是否能真正跑通,喜提来自懒惰的教育。
  3. 排查方法论:这次两个假设(offset 错、BS=0 回归)都是"看起来最像"的方向,逐个证伪后才逼出真正的根因。关键证据始终是 trace 里的两个时间差——0.3ms 的 90 01 和 734ms 的 50 02,它们证明了"复位后跑的是 APP 且 APP 完好",把排查方向从"刷写流程"扭转到"擦除操作本身"。
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇