文档元数据

项目 说明
芯片型号 BES1502P(内部代号 BES2710Y)
CPU 内核 ARM Cortex-M33(ARMv8-M 架构)
浮点单元 支持(单精度 FPU)
TrustZone 支持(ARM CMSE)
RTOS RTX5(CMSIS-RTOS2)
Flash 控制器 V7 版本
安全引导 支持(RSA3072 / ED25519)

目录

  • Part 1:硬件架构与存储系统

    • 1.1 内存映射

    • 1.2 TrustZone 安全/非安全分区

    • 1.3 存储介质、总线与 Cache 体系

  • Part 2:电源、时钟、模拟外设与 OTP/eFuse 管理

    • 2.1 关键硬件外设一览

    • 2.2 时钟管理初始化(hal_cmu_setup)

    • 2.3 PMU 电源管理初始化

    • 2.4 模拟驱动层

    • 2.5 OTP / eFuse 原理详解

  • Part 3:完整启动时序深度解析

    • 3.1 启动流程总览

    • 3.2 启动时序图

    • 3.3 Stage 1:硬件复位与 ROM 引导

    • 3.4 Stage 2:Flash 汇编入口(startup_main.S)

    • 3.5 Stage 3:NVIC_InitVectors 中断向量表初始化

    • 3.6 Stage 4:BootInit 核心引导初始化

    • 3.7 Stage 7:CMSE 安全世界主函数

    • 3.8 Stage 8:Non-Secure World main()

  • Part 4:系统核心并发机制与 RTOS

    • 4.1 RTX5 集成

    • 4.2 中断锁机制:int_lock_global vs osMutexWait

  • Part 5:存储布局与数据持久性策略

    • 5.1 Flash 存储结构

    • 5.2 关键数据结构

    • 5.3 供电域、RTC 与数据持久性

  • Part 6:工程参考手册与 FAQ

    • 6.1 源文件索引

    • 6.2 编译配置摘要

    • 6.3 常见问题解答(FAQ)


Part 1:硬件架构与存储系统

本部分从硬件视角梳理 BES1502P 的内存映射、TrustZone 分区、总线拓扑、Cache 机制及地址别名系统,是理解后续启动流程的基础。

1.1 内存映射

┌─────────────────────────────────────────────────────────────────────┐
│                    BES1502P Memory Map                               │
├────────────────────┬───────────────────────┬────────────────────────┤
│ Region             │ Address Range         │ Size                   │
├────────────────────┼───────────────────────┼────────────────────────┤
│ ROM (Code)         │ 0x00020000            │ 64 KB                  │
│ ROM (Data)         │ 0x22000000            │ 64 KB                  │
│ RAM0 (SRAM)        │ 0x20000000            │ 128 KB                 │
│ RAM1               │ 0x20020000            │ 128 KB                 │
│ RAM2               │ 0x20040000            │ 128 KB                 │
│ RAM3               │ 0x20060000            │ 256 KB                 │
│ RAM4               │ 0x200A0000            │ 256 KB                 │
│ RAM5               │ 0x200E0000            │ 256 KB                 │
│ RAM6               │ 0x20120000            │ 256 KB                 │
│ RAM7               │ 0x20160000            │ 256 KB                 │
│ RAM8               │ 0x201A0000            │ 256 KB                 │
│ RAM9               │ 0x201E0000            │ 128 KB                 │
│ RAMX0(执行区)    │ 0x00200000            │ 128 KB                 │
│ Flash (Data)       │ 0x2C000000            │ 可配置                 │
│ Flash (Execute)    │ 0x0C000000            │ 可配置                 │
│ PSRAM              │ 可配置                │ 可选                   │
├────────────────────┼───────────────────────┼────────────────────────┤
│ Flash Secure Zone  │ FLASHX_BASE           │ 512 KB (FLASH_S_SIZE)  │
│ Flash NS Zone      │ FLASHX_BASE+0x80000   │ 剩余空间               │
└────────────────────┴───────────────────────┴────────────────────────┘

1.2 TrustZone 安全/非安全分区

TrustZone 将 Flash 和 SRAM 从物理上分为 Secure(S)和 Non-Secure(NS)两个区域:

Flash Layout(TrustZone 已启用):
┌──────────────────────────────────────────────┐
│  Secure World (S)     │ 0x0C000000           │
│  ├── Boot Header      │ boot_struct_t        │
│  ├── CMSE Code        │ cmse_main.cpp        │
│  ├── Secure Drivers   │ hal_sec, mpc, SAU    │
│  └── NSC Veneers      │ Non-Secure Callable  │
│  大小:512 KB (0x80000)                      │
├──────────────────────────────────────────────┤
│  Non-Secure World (NS)│ 0x0C080000           │
│  ├── NS Boot Header   │ boot_struct_t        │
│  ├── main.cpp         │ App Entry            │
│  ├── RTOS Kernel      │ RTX5                 │
│  ├── BT Stack         │                      │
│  └── Application      │                      │
│  大小:Flash 剩余空间                         │
└──────────────────────────────────────────────┘

1.3 存储介质、总线与 Cache 体系

1.3.1 三类存储介质的本质差异
介质 断电保持 读速度 写速度 可擦写次数 典型用途
SRAM(片内) ✗ 易失 ~1 ns(零等待) ~1 ns 无限 代码/数据运行区、栈、堆
NOR Flash(片内) ✓ 非易失 ~50 ns(XIP) 极慢(ms/page) ~10 万次 程序存储、只读资源
PSRAM(片外) ✗ 易失 ~30 ns(XIP+Cache) ~30 ns 无限 大缓冲区、音频/图像数据

核心差异:Flash 是"仓库"——上电后数据仍在,但不能高速执行;SRAM 是"工作台"——极快但断电清空。程序必须在启动阶段将 Flash 中的热路径代码和数据"搬运"到 SRAM,这是 Boot 阶段 section 拷贝存在的根本原因。

1.3.2 冯诺依曼架构 vs 哈佛架构
冯诺依曼架构 (Von Neumann):
┌──────────┐    单总线    ┌──────────────────┐
│   CPU    │◄────────────►│ 统一存储          │
└──────────┘              │(代码 + 数据同一空间)│
                          └──────────────────┘
  缺点:取指与读数据共用总线 → 结构性冲突(Von Neumann Bottleneck)

哈佛架构 (Harvard):
              ┌─── 指令总线 ───► 指令存储
┌──────────┐  │
│   CPU    │──┤
└──────────┘  │
              └─── 数据总线 ───► 数据存储
  优点:取指与读数据并行 → 消除结构性冲突

ARM Cortex-M33 采用改进型哈佛架构:对程序员呈现统一的 32-bit 地址空间(形如冯诺依曼),但 CPU 内部有三条独立总线,实现哈佛的并行优势。

1.3.3 Cortex-M33 三总线架构
┌─────────────────────────────────────────────────────┐
              │                 Cortex-M33 Core                     │
              │                                                     │
              │  ┌──────────┐  ┌──────────┐  ┌──────────────────┐  │
              │  │ 指令取指  │  │ 数据访问  │  │  外设/大内存访问  │  │
              │  │(Fetch)   │  │(Load/Store)│  │                  │  │
              │  └────┬─────┘  └────┬──────┘  └────────┬─────────┘  │
              └───────┼─────────────┼──────────────────┼────────────┘
                      │             │                  │
                      ▼             ▼                  ▼
               ┌────────────┐ ┌────────────┐ ┌──────────────────┐
               │  ICode Bus  │ │  DCode Bus  │ │   System Bus     │
               │(指令总线) │ │(数据总线) │ │(系统总线 AHB)  │
               │ 0x00000000  │ │ 0x00000000  │ │  0x20000000+     │
               │ ~0x1FFFFFFF │ │ ~0x1FFFFFFF │ │                  │
               └─────┬──────┘ └──────┬─────┘ └────────┬─────────┘
                     │               │                 │
  • ICode Bus:专用于从 0x00000000~0x1FFFFFFF 取指令(只读)

  • DCode Bus:专用于从同一地址范围读写数据(Load/Store 指令)

  • System Bus:访问 SRAM(0x20000000+)、外设、Flash 数据别名

ICode 与 DCode 可同时工作,取指和读数据互不阻塞——这是改进型哈佛架构的核心价值。

1.3.4 地址别名系统:同一物理存储映射多个地址

这是理解整个存储系统的关键。同一块物理 Flash 被映射到 4 个不同的地址段,每个别名提供不同的访问属性:

物理 NOR Flash(片内, FLASH0)
        │
        ├─ 0x0C000000  FLASHX_BASE     (执行地址,可缓存,走 ICode Bus)  ← 代码执行
        ├─ 0x2C000000  FLASH_BASE      (数据地址,可缓存,走 DCode/System Bus)← 数据读
        ├─ 0x08000000  FLASHX_NC_BASE  (执行地址,不可缓存)← DMA/调试用
        └─ 0x28000000  FLASH_NC_BASE   (数据地址,不可缓存)← DMA/需要实时内容

物理 SRAM(RAM0~RAM9)
        │
        ├─ 0x20000000  RAM_BASE        (数据地址,走 System Bus)← 数据读写
        └─ 0x00200000  RAMX_BASE       (执行地址,走 ICode Bus) ← 搬运后代码执行

物理 ROM(片内, 64 KB)
        │
        ├─ 0x00020000  ROMX_BASE       (执行地址,ICode Bus)← ROM 代码执行
        └─ 0x22000000  ROM_BASE        (数据地址,System Bus)← 读 ROM 中的常量

物理 PSRAM(片外,SPI 接口)
        │
        ├─ 0x1C000000  PSRAMX_BASE     (执行地址,可缓存)
        ├─ 0x3C000000  PSRAM_BASE      (数据地址,可缓存)
        ├─ 0x18000000  PSRAMX_NC_BASE  (执行地址,不可缓存)
        └─ 0x38000000  PSRAM_NC_BASE   (数据地址,不可缓存)

为什么需要执行地址与数据地址之分?
ARM Cortex-M33 规定 ICode Bus 只能访问 0x00000000~0x1FFFFFFF(代码区),DCode/System Bus 访问 0x20000000+(数据区)。同一物理 Flash 映射两个地址段,使 CPU 既能通过 ICode 总线直接取指(XIP),又能通过 System Bus 读取 Flash 中的只读数据表。

// platform/hal/best1502p/plat_addr_map_best1502p.h
#define REAL_FLASHX_BASE    0x0C000000   // 执行别名(ICode 可达)
#define REAL_FLASH_BASE     0x2C000000   // 数据别名(System Bus)
#define REAL_FLASHX_NC_BASE 0x08000000   // 执行,不缓存
#define REAL_FLASH_NC_BASE  0x28000000   // 数据,不缓存

#define ROMX_BASE           0x00020000   // ROM 执行别名
#define ROM_BASE            0x22000000   // ROM 数据别名

#define RAMX0_BASE          0x00200000   // SRAM 执行别名
#define RAM0_BASE           0x20000000   // SRAM 数据别名
1.3.5 Cache 机制与初始化

Flash 原始读速度约 50~100 ns(26~104 MHz 接口),而 CPU 跑 208 MHz 时一个周期仅 ~5 ns。没有 Cache,每条指令需等待 10~20 个周期。

BES1502P Cache 配置(platform/hal/hal_cache.h):

  HAL_CACHE_ID_FLASH_EXEC  → I-Cache  (Flash 上的代码)
  HAL_CACHE_ID_FLASH_DATA  → D-Cache  (Flash 上的数据,如只读表)
  HAL_CACHE_ID_PSRAM_EXEC  → I-Cache  (PSRAM 上的代码)
  HAL_CACHE_ID_PSRAM_DATA  → D-Cache  (PSRAM 上的数据)

  Cache Line = 32 字节(标准)/ 64 字节(double linefill 模式)
CPU 取指流程(XIP,经 I-Cache):
  CPU → ICode Bus → I-Cache 查找
          │
          ├─ Cache Hit(命中):  直接返回,~1 ns
          │
          └─ Cache Miss(未命中):向 Flash 控制器发请求
                    │
                    └─ 从 Flash 读 32 字节 Cache Line(~8 cycles)
                       填入 Cache → 返回给 CPU
                       下次访问同一行:命中,零等待

  典型命中率:>95%,等效延迟接近 SRAM

Cache 初始化顺序(在 BootInit() 中):

void BootInit(void)
{
    mpu_boot_cfg();                                       // 先配 MPU
    hal_cache_enable(HAL_CACHE_ID_I_CACHE);              // 开 I-Cache
    hal_cache_enable(HAL_CACHE_ID_D_CACHE);              // 开 D-Cache
    hal_cache_writebuffer_enable(HAL_CACHE_ID_D_CACHE);  // D-Cache 写缓冲
    hal_cache_writeback_enable(HAL_CACHE_ID_D_CACHE);    // D-Cache 写回模式
    ...
}
// Cache 必须在 section 搬运之前开启,否则搬运本身会极慢

写策略说明

  • Write Buffer(写缓冲):CPU 写数据直接入队列即返回,由硬件异步刷入 SRAM/PSRAM

  • Write Back(写回):修改只在 Cache 中,Cache Line 被替换时才写回内存(最优性能)

  • Write Through(写直达):每次写同时更新 Cache 和内存(简单但慢,不启用)

1.3.6 NC(Non-Cacheable)地址别名的用途

NC 别名是解决 Cache 一致性(Cache Coherency) 问题的工具。当 CPU+Cache 与 DMA/其他处理器同时访问同一物理内存时,必须通过 NC 别名或显式 Cache 操作保证数据一致:

场景:DMA 直接读 Flash 数据到 SRAM
  ┌─────────────────────────────────────────────────────────────────┐
  │  错误做法:用 FLASH_BASE (0x2C000000, Cacheable)               │
  │    DMA 读的是物理 Flash,但 Cache 中可能有旧版本              │
  │    CPU 随后读同地址 → 命中 Cache → 读到脏数据(不一致!)      │
  │                                                                 │
  │  正确做法:用 FLASH_NC_BASE (0x28000000, Non-Cacheable)        │
  │    DMA 绕过 Cache 直接读物理 Flash                             │
  │    或:DMA 完成后调用 hal_cache_invalidate() 使对应行失效      │
  └─────────────────────────────────────────────────────────────────┘
1.3.7 完整存储层次图
CPU Core (Cortex-M33 @ 208 MHz)
                           │
              ┌────────────┼────────────┐
              │            │            │
         ICode Bus     DCode Bus   System Bus
         (取指)      (数据访问) (外设/SRAM)
              │            │            │
              └────────────┴────────────┘
                           │
              ┌────────────┴────────────┐
              │                         │
         ┌────┴────┐               ┌────┴────┐
         │ I-Cache  │               │ D-Cache  │
         │(代码缓存)│              │(数据缓存)│
         │ 命中→1 ns│               │ 命中→1 ns│
         └────┬────┘               └────┬────┘
              │ miss                    │ miss
              └────────────┬────────────┘
                           │
             ┌─────────────┴──────────────┐
             │       片内总线矩阵(AHB)   │
             │                            │
    ┌────────┴──────────┐    ┌────────────┴──┐
    │ Flash 控制器 V7   │    │ SRAM(~2 MB) │
    │ NOR Flash(内置) │    │ 零等待         │
    │ 读:~8 cycles     │    │ 断电清空       │
    │ 写:ms 级         │    └───────────────┘
    │ 断电不丢失        │    ┌───────────────┐
    └───────────────────┘    │ PSRAM 控制器V8│
    ┌───────────────────┐    │(外置)        │
    │ ROM(片内 64 KB) │    └───────────────┘
    │ BootLoader,只读  │
    └───────────────────┘

延迟汇总:
  SRAM 直接访问:            ~1 ns   (1 cycle @208 MHz)
  Flash XIP(Cache 命中):  ~5 ns   (1 cycle)
  Flash XIP(Cache 未命中):~40 ns  (8 cycles,linefill)
  Flash 原始读(无 Cache): ~50 ns+ (受 Flash 时钟限制)
  PSRAM(Cache 命中):      ~5 ns   (1 cycle)
  PSRAM(Cache 未命中):    ~30 ns  (XIP DQS 模式)
1.3.8 各类代码/数据的运行位置
代码/数据类型 存放位置 运行/访问位置 原因
普通应用代码 Flash (FLASHX) Flash XIP + I-Cache 体积大,Cache 命中率高,无需搬运
时序关键代码(.sram_text Flash → 搬运 SRAM (RAMX) 零等待,确定性延迟
Boot 阶段代码(.boot_text_sram Flash → 搬运 SRAM (RAMX) Flash 控制器未完全初始化时 SRAM 是唯一可靠执行区
NSC Veneer(.nsc_text Flash → 搬运 SRAM(安全属性区) SAU 须控制其安全属性,Flash XIP 地址不满足
全局变量(.data Flash(初值)→ 搬运 SRAM (RAM) 变量必须可写
零初始化变量(.bss 不存在于 Flash SRAM(清零) 无需存储,启动时清零
只读常量(const Flash(DATA 别名) Flash + D-Cache 无需写,留在 Flash 节约 SRAM
栈(Stack) SRAM 高端地址 频繁读写,必须在 SRAM
RTOS 堆(Heap) SRAM / PSRAM 动态分配,必须可写
音频 PCM 缓冲 PSRAM 体积大(>SRAM 容量),低延迟要求
1.3.9 冯诺依曼瓶颈的规避策略

BES1502P 通过以下多层机制应对总线带宽瓶颈:

  1. 分离总线(哈佛特性):ICode 取指与 DCode 读数据同时进行,消除总线争用

  2. Cache:将热点代码/数据的访问延迟从 50 ns 压缩到 ~5 ns,减少 90% 的总线流量

  3. SRAM 执行区:热路径代码直接在 SRAM 运行,零等待、零缓存 miss

  4. NC 别名:DMA 传输使用非缓存地址,不污染 CPU 的 Cache 空间

  5. PSRAM XIP:外置 PSRAM 通过 SPI 接口支持 XIP + Cache,扩展可执行内存空间

  6. Write Buffer:CPU 写操作不等待 SRAM 完成,继续执行下一条指令

实际性能参考(@208 MHz):
  纯 SRAM 执行:     理论峰值 ~208 MIPS(IPC ≈ 1)
  Flash XIP + Cache:热循环 ~150~190 MIPS(高命中率)
  Flash XIP 无 Cache:~20~40 MIPS(严重受 Flash 速度限制)
  → Cache 开启后性能提升 4~8 倍,这是 BootInit() 最优先开启 Cache 的原因

Part 2:电源、时钟、模拟外设与 OTP/eFuse 管理

本部分介绍芯片的电源管理单元(PMU)、时钟管理单元(CMU)、模拟外设驱动以及一次性可编程存储(OTP/eFuse)的工作原理。这些子系统在启动早期即被初始化,是整个系统稳定运行的基础。

2.1 关键硬件外设一览

外设 说明
PMU 电源管理单元(片内集成)
CMU 时钟管理单元
ISPI 内部 SPI(模拟寄存器访问总线)
NOR Flash 片内 Flash 控制器 V7
PSRAM 外置 PSRAM 控制器 V8
DMA 音频 DMA + 通用 DMA
CODEC 片内音频编解码器
BT Core 蓝牙双模控制器
USB USB 2.0 OTG

2.2 时钟管理初始化(hal_cmu_setup)

源文件platform/hal/hal_cmu_common.c:576

2.2.1 时钟树结构
┌─────────────┐
            │  26 MHz XTAL │  ← 外部晶振
            └──────┬───────┘
                   │
         ┌─────────┼──────────┐
         │         │          │
         ▼         ▼          ▼
    ┌─────────┐ ┌────────┐ ┌────────┐
    │  X2     │ │  X4    │ │  PLL   │
    │  52 MHz │ │ 104 MHz│ │ BBPLL  │
    └────┬────┘ └───┬────┘ └───┬────┘
         │          │          │
         └──────────┼──────────┘
                    │
              ┌─────┴─────┐
              │  CMU MUX   │
              └─────┬─────┘
                    │
    ┌───────┬───────┼───────┬───────┐
    │       │       │       │       │
    ▼       ▼       ▼       ▼       ▼
  SYS_CLK FLASH_CLK MEM_CLK PERIPH  UART
  (CPU)  (NorFlash) (PSRAM) (SPI)  (Baud)
2.2.2 hal_cmu_setup() 完整流程
void hal_cmu_setup(void)
{
    // [1] IOMUX 默认配置
    hal_iomux_set_default_config();

    // [2] JTAG 调试接口(仅 DEBUG 模式)
    hal_iomux_set_jtag();
    hal_cmu_jtag_clock_enable();

    // [3] 系统定时器(用于延时的基础时基)
    hal_sys_timer_open();

    // [4] Boot Mode 初始化(读取硬件启动模式寄存器)
    hal_hw_bootmode_init();

    // [5] Flash 时钟设置(初始 26 MHz,保守频率)
    hal_norflash_set_boot_freq(HAL_CMU_FREQ_26M);

    // [6] 内存时钟设置
    hal_cmu_mem_set_freq(HAL_CMU_FREQ_26M);

    // [7] 系统时钟设置
    hal_cmu_sys_set_freq(HAL_CMU_FREQ_26M);

    // [8] 模块时钟状态初始化
    hal_cmu_module_init_state();

    // [9] ISPI 时钟配置(用于访问模拟寄存器)
    hal_cmu_ispi_set_freq(HAL_CMU_PERIPH_FREQ_26M);

    // [10] 打开 ISPI 通道(模拟接口使能)
    hal_analogif_open();

    // [11] 芯片 ID 识别(读取 metal ID / revision)
    hal_chipid_init();

    // [12] PMU Boot 初始化(电源基础配置)
    pmu_boot_init();

    // [13] 振荡器倍频使能
    hal_cmu_osc_x2_enable();    // 52 MHz
    hal_cmu_osc_x4_enable();    // 104 MHz
}
2.2.3 ISPI 通信协议

BES1502P 通过内部 SPI(ISPI)访问模拟寄存器,数字域与模拟域之间完全解耦:

┌────────────┐     ISPI Bus      ┌────────────┐
│  Digital   │◄──────────────────►│  Analog    │
│  Core      │   (Reg R/W)     │  Domain    │
│  (CMU)     │                   │  (PMU/RF)  │
└────────────┘                   └────────────┘

Register Access:
  ana_read(reg, &val)  → hal_analogif_reg_read(ANA_REG(reg), val)
  ana_write(reg, val)  → hal_analogif_reg_write(ANA_REG(reg), val)
  rf_read(reg, &val)   → hal_analogif_reg_read(RF_REG(reg), val)

2.3 PMU 电源管理初始化

源文件platform/drivers/ana/best1502p/pmu_best1502p.c

2.3.1 PMU 架构
VBAT(Battery 3.0~4.2 V)
                         │
           ┌─────────────┼─────────────┐
           │             │             │
           ▼             ▼             ▼
    ┌────────────┐ ┌────────────┐ ┌────────────┐
    │ DCDC_DIG   │ │ DCDC_ANA   │ │ DCDC_HPPA  │
    │(数字核)  │ │(模拟核)  │ │(耳机功放) │
    │ 0.65~1.1 V │ │ 1.4~1.9 V  │ │ 1.4~1.9 V  │
    └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
          │              │              │
          ▼              ▼              ▼
    ┌──────────┐   ┌──────────┐   ┌──────────┐
    │CPU/SRAM  │   │Codec/PLL │   │Headphone │
    │Flash/IO  │   │RF/XTAL   │   │Amplifier │
    └──────────┘   └──────────┘   └──────────┘
2.3.2 pmu_boot_init():早期 PMU 初始化

hal_cmu_setup() 中被调用,负责上电初期最基础的电源配置:

void pmu_boot_init(void)
{
    // [1] 读取硬件版本(metal ID)
    read_hw_metal_id();

    // [2] 判断 Boot 原因:POR / WDT / Charger / PowerKey
    pmu_boot_cause_init();

    // [3] 读取 eFuse 校准值(DCDC 电压补偿 / LDO 电压补偿)
    pmu_get_dcdc_calib_value();
    pmu_get_ldo_dig_calib_value();

    // [4] 禁用 LDO 硬件 Ramp(避免电压缓慢爬升影响启动速度)

    // [5] 基础电源配置(VCORE / VANA / VMEM 初始电平设置)
}
2.3.3 pmu_open():完整 PMU 初始化

main() 中被调用,完成 PMU 的全量初始化:

int pmu_open(void)
{
    // [1] 防重入保护
    if (pmu_opened) return 0;
    pmu_opened = true;

    // [2] 电压断言检查
    ASSERT(vcodec_mv == 1400~1900);
    ASSERT(vhppa_mv  == 1400~1900);

    // [3] eFuse 初始化
    pmu_efuse_init();

    // [4] 清除所有 PMU 中断
    pmu_write(PMU_REG_INT_MASK, 0);
    pmu_write(PMU_REG_INT_EN, 0);

    // [5] 电源键配置(允许 PMU 在按键按下时进入 Sleep)

    // [6] Bandgap 配置(低功耗模式下使用 small bandgap)

    // [7] 32K 时钟源选择(外部 32.768 KHz 或内部 RC)

    // [8] DCDC 模式配置(PWM / PFM 自动切换)

    // [9] 各 LDO 模块使能(VCODEC / VMEM / VIO 等)

    // [10] 充电器检测配置(PMU_CHARGER_PLUGIN / PLUGOUT 中断)

    return 0;
}
2.3.4 关键时序参数
#define PMU_LDO_PU_STABLE_TIME_US       1800  // LDO 上电稳定:1.8 ms
#define PMU_LDO_PULLDOWN_STABLE_TIME_US 1000  // LDO 下拉稳定:1 ms
#define PMU_DCDC_PU_STABLE_TIME_US      100   // DCDC 上电稳定:100 us
#define PMU_VANA_STABLE_TIME_US         10    // VANA 稳定:10 us
#define PMU_VCORE_STABLE_TIME_US        10    // VCORE 稳定:10 us
#define PMU_BIG_BG_STABLE_TIME_US       200   // 大 Bandgap 稳定:200 us
#define PMU_EXT_32K_STABLE_TIME         300   // 32K 晶振稳定:300 ms

2.4 模拟驱动层

2.4.1 驱动文件架构
文件 功能
pmu_best1502p.c 电源管理(DCDC/LDO/充电/休眠)
analog_best1502p.c 模拟外设(CODEC/ADC/DAC)
charger_best1502p.c 充电管理(CC/CV/OVP)
rf_xtal_best1502p.c RF 和晶振校准
usbphy_best1502p.c USB PHY 控制
analog_sens_best1502p.c Sensor Hub 模拟
2.4.2 寄存器访问模型
// 所有模拟寄存器通过 ISPI 总线访问
#define ana_read(reg,val)   hal_analogif_reg_read(ANA_REG(reg), val)
#define ana_write(reg,val)  hal_analogif_reg_write(ANA_REG(reg), val)
#define rf_read(reg,val)    hal_analogif_reg_read(RF_REG(reg), val)
#define rf_write(reg,val)   hal_analogif_reg_write(RF_REG(reg), val)
// ANA_REG / RF_REG 区分模拟/射频寄存器地址空间
2.4.3 PMU 电源模式
┌─────────────────────────────────────────────────────────┐
│  PMU Power Modes                                        │
├─────────────────────────────────────────────────────────┤
│  Active Mode:                                           │
│    CPU 全速运行,所有外设可用                           │
│    DCDC_DIG = 0.9 V(正常)/ 1.1 V(高性能)           │
│    系统时钟:最高 208 MHz                               │
│                                                         │
│  Light Sleep:                                           │
│    CPU WFI,外设处于待机                                │
│    DCDC_DIG → LP 模式(0.65 V~0.75 V)                 │
│    唤醒:任意使能中断                                   │
│                                                         │
│  Deep Sleep:                                            │
│    CPU 断电,SRAM 保持数据                              │
│    仅 PMU + 32K 时钟运行                                │
│    唤醒:GPIO / Timer / Charger                         │
│                                                         │
│  Hibernate:                                             │
│    仅 VRTC 域存活                                       │
│    极低电流(< 1 μA)                                   │
│    唤醒:仅电源键                                       │
└─────────────────────────────────────────────────────────┘

2.5 OTP / eFuse 原理详解

2.5.1 什么是 OTP / eFuse

OTP(One-Time Programmable)和 eFuse 都是只能写一次、永久保存的非易失性存储单元,本质是同一类技术的不同叫法:

  • eFuse(电熔丝):出厂时每个 bit 为"0"(熔丝完整)。编程时向特定地址施加高电流,将熔丝烧断,该 bit 变为"1",不可逆

  • OTP:泛指一次性可编程存储器,eFuse 是其一种实现方式。BES1502P 文档通常混用这两个词。

物理存储结构(eFuse bit):
  ┌──────────────────────────────────────────┐
  │  未编程态:熔丝完整 → bit = 0            │
  │  ┌──┐                                    │
  │  │==│ ← 完整的金属熔丝                  │
  │  └──┘                                    │
  │                                          │
  │  已编程态:熔丝烧断 → bit = 1            │
  │  ┌  ┐                                    │
  │  │  │ ← 熔断缺口(不可逆)              │
  │  └  ┘                                    │
  └──────────────────────────────────────────┘
2.5.2 BES1502P eFuse 存储的内容

eFuse 在芯片生产/测试阶段由厂商或产线烧录,启动时由 ROM 读取:

// platform/drivers/ana/best1502p/pmu_best1502p.c
// eFuse Page 分配:
PMU_EFUSE_PAGE_DCDC1_VOLT   // DCDC 数字核(CPU)电压校准补偿值
PMU_EFUSE_PAGE_DCDC2_VOLT   // DCDC 模拟核(Codec/PLL)电压校准补偿值
PMU_EFUSE_PAGE_DCDC3_VOLT   // DCDC HPPA(耳机功放)电压校准补偿值
PMU_EFUSE_PAGE_LDO_CORE     // LDO CORE 电压校准补偿值
// + wafer ID / metal ID(芯片版本标识)
// + Secure Boot 公钥哈希(启用 Secure Boot 时)

为什么要存电压校准值? 芯片制造工艺存在偏差(工艺角),同批芯片的最优工作电压略有不同。测试时每颗单独测量,将补偿值烧入 eFuse,固件启动时读出并微调 DCDC/LDO 输出,保证全批次在最优电压下工作。

2.5.3 OTP 读取时机与流程
上电
  │
  ▼
ROM BootLoader(0x00020000)
  │
  ├─ [1] 读取 wafer ID / metal ID
  │       → hal_chipid_init()(在 hal_cmu_setup() 中调用)
  │       → 确定芯片版本,选择对应驱动参数
  │
  ├─ [2] 读取 DCDC/LDO 校准值
  │       → pmu_boot_init() → pmu_get_dcdc_calib_value()
  │       → 调整 DCDC 输出电压,保证 CPU/SRAM 稳定供电
  │
  ├─ [3] 读取 Secure Boot 公钥哈希(可选)
  │       → 验证 Flash 中 boot_struct_t 的 RSA3072 公钥合法性
  │       → 防止攻击者替换公钥来绕过验签
  │
  └─ [4] 读取 Dolby 私钥地址
          → dlb_cmse_setup(DOLBY_PRIVATE_KEY_ADDR)
          → OTP 地址 0x38 存放 Dolby 解密私钥
2.5.4 eFuse 安全属性
安全属性 说明
不可逆 bit 只能 0→1,攻击者无法擦除
读保护 敏感 page(如密钥哈希)可设置"读锁",锁定后 CPU 也无法读出明文
写保护 每个 page 可单独设置"写锁",防止二次编程
TrustZone 隔离 密钥相关 eFuse page 仅在 Secure World 可访问

Part 3:完整启动时序深度解析

本部分按时间顺序逐阶段解析从上电复位到 RTOS 调度器就绪的完整启动流程,涵盖汇编入口、向量表初始化、Boot 初始化、TrustZone 安全世界以及非安全世界的主函数。

3.1 启动流程总览

┌─────────────────────────────────────────────────────────────────────┐
│                    POWER ON RESET                                    │
│                         │                                           │
│                         ▼                                           │
│              ┌──────────────────┐                                   │
│              │  HW Reset        │  ← POR / BOR / WDT / Pin Reset   │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  ROM BootLoader   │  ← 片内 ROM 执行,读取 OTP 配置  │
│              │  (0x00020000)     │                                   │
│              └────────┬─────────┘                                   │
│                       │  跳转到 Flash                               │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  Boot_Loader()   │  ← startup_main.S                 │
│              │  (Flash Entry)   │     设置 MSP,初始化硬件           │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  NVIC_InitVectors│  ← 中断向量表重定位               │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  SystemInit()    │  ← 系统时钟初始化                  │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  BootInit()      │  ← Cache / MPU / RAM 搬运         │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  _start / main   │  ← C 运行时初始化                  │
│              │  (Secure World)  │                                   │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  CMSE_MAIN()     │  ← 安全世界主函数                  │
│              │  ├─ PMU Init     │     电源管理初始化                  │
│              │  ├─ Flash Init   │     Flash 安全配置                  │
│              │  ├─ SEC Init     │     安全外设初始化                  │
│              │  ├─ MPC Init     │     内存保护控制器                  │
│              │  ├─ SAU Setup    │     安全属性单元                   │
│              │  ├─ DLB Verify   │     Dolby 验签解密                 │
│              │  └─ Secure Boot  │     用户安全引导校验               │
│              └────────┬─────────┘                                   │
│                       │  跳转 NS 世界                               │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  NS Reset Handler│  ← 非安全世界入口                  │
│              │  (0x0C080000+off)│                                   │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  NS BootInit()   │  ← 非安全世界 Boot                 │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  NS main()       │  ← main.cpp                       │
│              │  ├─ WDT Init     │     看门狗                         │
│              │  ├─ HW Setup     │     GPIO / IOMUX                  │
│              │  ├─ DMA Open     │     DMA 通道                       │
│              │  ├─ Trace Init   │     调试串口                       │
│              │  ├─ PMU Open     │     电源完整初始化                  │
│              │  ├─ Analog Open  │     模拟外设                       │
│              │  └─ app_init()   │     应用层初始化                   │
│              └────────┬─────────┘                                   │
│                       │                                             │
│                       ▼                                             │
│              ┌──────────────────┐                                   │
│              │  RTOS Scheduler  │  ← RTX5 多任务调度                 │
│              │  ├─ BT Task      │                                   │
│              │  ├─ Audio Task   │                                   │
│              │  ├─ App Task     │                                   │
│              │  └─ Idle Task    │                                   │
│              └──────────────────┘                                   │
└─────────────────────────────────────────────────────────────────────┘

3.2 启动时序图

3.2.1 上电到 RTOS 就绪时序
时间轴(近似值):
t=0 ms       t≈0.5 ms     t≈2 ms       t≈5 ms        t≈20 ms       t≈50 ms
  │             │             │             │              │              │
  ▼             ▼             ▼             ▼              ▼              ▼
┌─────┐    ┌─────────┐  ┌─────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
│POR  │    │ROM Boot │  │Flash    │  │CMSE Main │  │NS Boot   │  │RTOS      │
│Reset│    │OTP Read │  │Boot_Ldr │  │Secure    │  │main()    │  │Scheduler │
│     │    │SecCheck │  │BootInit │  │Init      │  │app_init  │  │Ready     │
└─────┘    └─────────┘  └─────────┘  └──────────┘  └──────────┘  └──────────┘
  │             │             │             │              │              │
  │  XTAL 稳定  │  Cache/RAM  │  PMU/MPC   │  BT Init    │  任务创建     │
  │  DCDC 启动  │  搬运完成   │  SAU 配置  │  Audio Init │  开始调度     │
  │  Bandgap 稳 │  时钟切换   │  NS 跳转   │  WDT 喂狗   │              │
3.2.2 中断响应时序
ARM Cortex-M33 中断响应:
  中断触发 → 硬件自动压栈(xPSR, PC, LR, R12, R3-R0)
           → 取向量(从 VTOR + IRQ# × 4)
           → 跳转 ISR
           → ISR 执行
           → 异常返回(EXC_RETURN)
           → 硬件自动弹栈

  响应延迟:12 cycles(zero wait state)
  尾链优化:6 cycles(中断嵌套场景)

TrustZone 额外开销:
  S → NS 切换:   ~24 cycles(寄存器清零 + 安全状态保存)
  NS → S 调用(NSC veneer):~16 cycles

3.3 Stage 1:硬件复位与 ROM 引导

3.3.1 Power-On Reset(POR)

当电池/充电器为芯片供电后,硬件层面依次发生:

  1. 电源域上电顺序

    • VBAT 上电 → PMU 内部 BOR(Brown-Out Reset)检测

    • PMU 稳压器启动:DCDC_DIG → DCDC_ANA → LDO_VCORE

    • 晶振起振等待(26 MHz XTAL,约 300 us 稳定时间)

  2. 复位释放

    • PMU 释放全局复位信号

    • ARM Core 从 SCB->VTOR 指向的地址读取初始 MSP 和 Reset_Handler

  3. OTP/eFuse 读取

    • 片内 ROM 读取 OTP 区域

    • 获取 DCDC 校准值、LDO 补偿值、Secure Boot 公钥哈希

    • 获取 wafer ID、metal ID 用于芯片版本识别

// OTP/eFuse 校准数据结构(pmu_best1502p.c)
// PMU_EFUSE_PAGE_DCDC1_VOLT:DCDC 数字核校准补偿
// PMU_EFUSE_PAGE_DCDC2_VOLT:DCDC 模拟核校准补偿
// PMU_EFUSE_PAGE_DCDC3_VOLT:DCDC HPPA 校准补偿
// PMU_EFUSE_PAGE_LDO_CORE:  LDO CORE 电压校准
3.3.2 ROM Boot Loader

片内 ROM(64 KB @ 0x00020000)负责:

  1. 验证 Flash 中的 Boot Magic Number(0xBE57EC1C

  2. 如果启用了 Secure Boot,验证 Flash 镜像签名

  3. 确定跳转地址(Flash 入口或 Programmer 模式)

// Boot Header 结构(tool_msg.h)
struct boot_hdr_t {
    unsigned int magic;            // 必须为 0xBE57EC1C
    unsigned short flag;           // 安全引导标志
    unsigned short version;        // 版本信息
    unsigned int reserved_008;
    unsigned int build_info_start; // 构建信息指针
};

struct boot_struct_t {
    struct boot_hdr_t hdr;
    unsigned short security;       // 安全设置
    unsigned short reserved_012;
    unsigned char key[MAX_KEY_LEN]; // RSA3072 公钥(384+384+4 bytes)
    unsigned char sig[MAX_SIG_LEN]; // RSA3072 签名(384 bytes)
};

3.4 Stage 2:Flash 汇编入口(startup_main.S)

源文件platform/main/startup_main.S

这是 Flash 中第一段被执行的代码,以纯汇编实现:

Boot_Loader:
    ldr     r0, =.L_boot_loader_start
    bx      r0                          ; 跳转到代码地址空间

.L_boot_loader_start:
    ; [1] boot_loader_entry_hook(BOOT_LOADER_ENTRY_HOOK=1 时执行)
    bl      boot_loader_entry_hook

    ; [2] 设置主堆栈指针 MSP
    ldr     r0, =__StackTop
    msr     msp, r0

    ; [3] ARMv8-M 堆栈限制寄存器
    ldr     r0, =__StackLimit
    msr     msplim, r0

    ; [4] 使用 MSP,设置特权模式
    movs    r0, #0
    msr     control, r0
    isb

    ; [5] 中断向量表初始化
    bl      NVIC_InitVectors

    ; [6] 系统初始化(时钟)
    bl      SystemInit

    ; [7] 引导初始化(Cache,RAM 搬运)
    bl      BootInit

    ; [8] 清零 BSS 段(NOSTD 模式下由此汇编完成)

    ; [9] 跳转 C 运行时
    bl      _start          ; → 最终调用 main() / MAIN_ENTRY()
3.4.1 boot_loader_entry_hook

BES1502P 配置了 BOOT_LOADER_ENTRY_HOOK=1,此 hook 在 MSP 设置前执行,用于:

  • 切换 SP 到正确的 RAM bank

  • 处理 RAM boot 模式

  • 早期 GPIO 状态配置

3.4.2 Stack Pointer 设置
MSP(Main Stack Pointer):
  __StackTop   = RAM 最高地址(由链接脚本定义)
  __StackLimit = Stack 下界(ARMv8-M 硬件栈溢出检测)

MSPLIM 寄存器是 ARMv8-M 新增功能:
  当 SP < MSPLIM 时,硬件自动触发 UsageFault,无需软件检测栈溢出
3.4.3 CONTROL 寄存器
CONTROL = 0:
  bit[1] SPSEL = 0  → 使用 MSP(非 PSP)
  bit[0] nPRIV = 0  → 特权模式

所有 Boot 阶段运行在特权 + MSP 模式。
RTOS 启动后,任务切换到 PSP + 非特权模式。

3.5 Stage 3:NVIC_InitVectors 中断向量表初始化

源文件platform/cmsis/cmsis_nvic.c

3.5.1 中断向量表结构
// Flash 中的只读向量表(初始模板)
static const uint32_t fault_handlers[NVIC_USER_IRQ_OFFSET] = {
    (uint32_t)__stack,              // [0]  初始 MSP 值
    (uint32_t)Reset_Handler,        // [1]  复位处理
    (uint32_t)NMI_Handler,          // [2]  NMI
    (uint32_t)HardFault_Handler,    // [3]  硬故障
    (uint32_t)MemManage_Handler,    // [4]  内存管理故障
    (uint32_t)BusFault_Handler,     // [5]  总线故障
    (uint32_t)UsageFault_Handler,   // [6]  用法故障
    (uint32_t)SecureFault_Handler,  // [7]  安全故障(ARMv8-M 新增)
    (uint32_t)NVIC_default_handler, // [8]  Reserved
    (uint32_t)NVIC_default_handler, // [9]  Reserved
    (uint32_t)NVIC_default_handler, // [10] Reserved
    (uint32_t)SVC_Handler,          // [11] SVC(RTOS 系统调用)
    (uint32_t)DebugMon_Handler,     // [12] 调试监控
    (uint32_t)NVIC_default_handler, // [13] Reserved
    (uint32_t)PendSV_Handler,       // [14] PendSV(RTOS 任务切换)
    (uint32_t)SysTick_Handler,      // [15] SysTick(RTOS 时间片)
};
3.5.2 向量表重定位
void NVIC_InitVectors(void)
{
    // 将向量表从 Flash 复制到 RAM(可动态修改)
    for (i = 0; i < NVIC_NUM_VECTORS; i++) {
        vector_table[i] = (i < ARRAY_SIZE(fault_handlers)) ?
            fault_handlers[i] : (uint32_t)NVIC_default_handler;
    }
    // 设置 VTOR 指向 RAM 向量表
    SCB->VTOR = (uint32_t)vector_table;
    __DSB();  // 数据同步屏障,确保写入生效
}

为什么要重定位到 RAM?

  • RAM 中的向量表可以动态修改(运行时注册中断处理函数)

  • RTOS 需要注册 SVC_Handler、PendSV_Handler、SysTick_Handler

  • 外设驱动需要注册各自的 IRQ Handler

3.5.3 BES1502P 中断源列表(部分)
IRQ# 名称 说明
0 FPU_IRQn 浮点运算异常
1 SDMMC_IRQn SD/MMC 控制器
2 AUDMA_IRQn 音频 DMA
3 GPDMA_IRQn 通用 DMA
4 FLASH_IRQn Flash 控制器
5 WAKEUP_IRQn 唤醒
6-7 USB_IRQn USB
8-11 I2C_IRQn I2C
12-15 SPI_IRQn SPI
16-18 UART_IRQn UART
... BT_IRQn 蓝牙控制器
... CODEC_IRQn 音频编解码
... PMU_IRQn 电源管理

3.6 Stage 4:BootInit 核心引导初始化

源文件platform/cmsis/system_utils.c:380

3.6.1 完整执行序列
void BootInit(void)
{
    // ═══ Phase 1:MPU 初始化 ═══
    mpu_boot_cfg();           // 配置内存保护区域

    // ═══ Phase 2:Cache 使能 ═══
    hal_cache_enable(HAL_CACHE_ID_I_CACHE);             // 指令缓存
    hal_cache_enable(HAL_CACHE_ID_D_CACHE);             // 数据缓存
    hal_cache_writebuffer_enable(HAL_CACHE_ID_D_CACHE); // 写缓冲
    hal_cache_writeback_enable(HAL_CACHE_ID_D_CACHE);   // 回写模式

    // ═══ Phase 3:GOT 基址初始化 ═══
    GotBaseInit();            // 位置无关代码支持

    // ═══ Phase 4:Boot Section 搬运 ═══
    boot_init_boot_sections(); // Flash → SRAM(Boot 代码段)

    // ═══ Phase 5:时钟系统初始化 ═══
    hal_cmu_setup();          // 完整时钟树配置

    // ═══ Phase 6:SRAM Section 搬运 ═══
    boot_init_sram_sections(); // Flash → SRAM(运行时代码/数据)

    // ═══ Phase 7:安全段初始化(仅 ARM_CMSE)═══
    boot_init_security_sections(); // NSC veneer 代码搬运

    // ═══ Phase 8:PSRAM 初始化(如果启用)═══
    pmu_open();               // 先开 PMU 给 PSRAM 供电
    hal_psram_init();         // PSRAM 控制器初始化
    boot_init_psram_sections(); // PSRAM 代码/数据搬运
}
3.6.2 Memory Section 搬运详解

链接脚本定义了多个 section,需要从 Flash 搬运到 RAM 才能运行:

┌─────────────────────────────────────────────────────┐
│  Flash(LMA - Load Memory Address)                 │
│  ┌──────────────────┐                               │
│  │ .boot_text_sram  │───┐                           │
│  │ .boot_data_sram  │───┼──→ RAM(VMA)             │
│  ├──────────────────┤   │    ┌──────────────────┐   │
│  │ .sram_text       │───┤    │ .boot_text_sram  │   │
│  │ .sram_data       │───┤    │ .boot_data_sram  │   │
│  ├──────────────────┤   │    │ .sram_text       │   │
│  │ .fast_sram_text  │───┤    │ .sram_data       │   │
│  ├──────────────────┤   │    │ .fast_sram_text  │   │
│  │ .nsc_text (CMSE) │───┘    │ .nsc_text        │   │
│  └──────────────────┘        └──────────────────┘   │
│                                                     │
│  搬运原因:                                          │
│  1. Flash 读取速度慢(~26 MHz),SRAM 可全速执行     │
│  2. Boot 阶段 Flash 控制器未完全配置,RAM 是唯一可靠执行区 │
│  3. 时序关键代码需要零等待执行                       │
│  4. NSC veneer 必须在特定安全属性的 RAM 区域         │
└─────────────────────────────────────────────────────┘
// 搬运实现(system_utils.c)
void boot_init_boot_sections(void)
{
    uint32_t *dst, *src;
    for (dst = __boot_sram_start__, src = __boot_sram_start_flash__;
            src < __boot_sram_end_flash__;
            dst++, src++) {
        *dst = *src;
    }
}

3.7 Stage 7:CMSE 安全世界主函数

源文件platform/main/cmse_main.cpp

3.7.1 TrustZone 安全模型
┌─────────────────────────────────────────────────────────────────┐
│  ARM TrustZone Architecture                                     │
│                                                                 │
│  ┌─────────────────────┐    ┌─────────────────────────────────┐ │
│  │   Secure World (S)  │    │   Non-Secure World (NS)         │ │
│  │                     │    │                                 │ │
│  │  - Boot Loader      │    │  - Application (main.cpp)       │ │
│  │  - Secure Boot      │    │  - RTOS (RTX5)                  │ │
│  │  - Key Storage      │    │  - BT Stack                     │ │
│  │  - Crypto Engine    │    │  - Audio                        │ │
│  │  - OTP Access       │    │  - User App                     │ │
│  │  - MPC/SAU Config   │    │                                 │ │
│  │                     │    │                                 │ │
│  │     NSC Veneer      │    │                                 │ │
│  │  ┌───────────────┐  │    │                                 │ │
│  │  │cmse_nonsecure  │◄─┼────┼── NS 通过 veneer 调用 S 函数   │ │
│  │  │   _entry       │  │    │                                 │ │
│  │  └───────────────┘  │    │                                 │ │
│  └─────────────────────┘    └─────────────────────────────────┘ │
│                                                                 │
│  硬件隔离:SAU + MPC + NVIC.ITNS 三重保护                        │
└─────────────────────────────────────────────────────────────────┘
3.7.2 CMSE Main 执行流程
int MAIN_ENTRY(void)  // Secure World 入口
{
    // ═══ [1] DMA 通道打开 ═══
    hal_audma_open();
    hal_gpdma_open();

    // ═══ [2] 调试串口初始化(仅 DEBUG)═══
    cmse_trace_init();
    hal_iomux_set_uart0/1/2();
    hal_trace_open(HAL_TRACE_TRANSPORT_UARTx);

    // ═══ [3] Flash 安全初始化 ═══
    sec_flash_init(HAL_FLASH_ID_0);

    // ═══ [4] Flash ID 显示 ═══
    hal_norflash_show_id_state(HAL_FLASH_ID_0, true);

    // ═══ [5] PMU 完整初始化 ═══
    pmu_open();

    // ═══ [6] 左右耳识别(GPIO 板级 ID)═══
    cmse_BoardidInit();  // P0_3 GPIO 读取

    // ═══ [7] UART 通信测试 ═══
    uart_init_cmse();    // UART1 @ 38400 bps
    // 左耳发送 0xAA*4(9次),右耳发送 0xBB*4(6次)

    // ═══ [8] 提升系统频率 ═══
    hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_104M);

    // ═══ [9] 安全外设初始化 ═══
    hal_sec_init();

    // ═══ [10] MPC(Memory Protection Controller)═══
    mpc_init();

    // ═══ [11] SAU(Security Attribution Unit)═══
    TZ_SAU_Setup();
    __ISB();

    // ═══ [12] Dolby 解密区域加载 ═══
    tz_customer_load_ram_ramx_section_info_t info;
    info.flash_addr = inc_enc_bin_sec_start;
    info.dst_text_sramx_addr_start = __customer_load_sram_text_start__;
    // ... 配置地址
    sec_decrypted_section_load_init(&info);

    // ═══ [13] Dolby 安全启动验证 ═══
    dlb_cmse_setup(DOLBY_PRIVATE_KEY_ADDR);  // OTP 地址 0x38

    // ═══ [14] 用户安全引导校验(可选)═══
    #ifdef USER_SECURE_BOOT
    if (user_secure_boot_check() != 0) {
        ASSERT(0, "security check fail");
    }
    #endif

    // ═══ [15] 释放高频请求,准备跳转 ═══
    hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_32K);

    // ═══ [16] 计算 NS 入口地址 ═══
    uint32_t ns_app_start_addr = FLASHX_BASE + NS_APP_START_OFFSET;
    // NS_APP_START_OFFSET = FLASH_S_SIZE = 0x80000(512 KB)

    // ═══ [17] 验证 NS 镜像 Magic ═══
    if (*(uint32_t *)ns_app_start_addr != BOOT_MAGIC_NUMBER) {
        TRACE(0, "nonsec image magic error");
    } else {
        // ═══ [18] 跳转 Non-Secure World ═══
        funcptr_void NonSecure_ResetHandler;
        NonSecure_ResetHandler = (funcptr_void)(
            (uint32_t)(&((struct boot_struct_t *)ns_app_start_addr)->hdr + 1)
        );
        NonSecure_ResetHandler();  // 永不返回
    }

    SAFE_PROGRAM_STOP();
}
3.7.3 SAU(Security Attribution Unit)配置
SAU 区域配置:
┌─────────────────────────────────────────────────────┐
│ Region │ Base Addr    │ Limit Addr   │ Attribute    │
├────────┼──────────────┼──────────────┼──────────────┤
│ 0      │ NS Flash     │ NS Flash End │ Non-Secure   │
│ 1      │ NS RAM       │ NS RAM End   │ Non-Secure   │
│ 2      │ Peripherals  │ Periph End   │ Non-Secure   │
│ 3      │ NSC Veneer   │ NSC End      │ NS-Callable  │
└────────┴──────────────┴──────────────┴──────────────┘

未被 SAU 覆盖的区域默认为 Secure。
3.7.4 MPC(Memory Protection Controller)

MPC 以 block 为粒度控制 SRAM 和 Flash 的安全属性:

MPC 配置 Flash:
  [0x0C000000 ~ 0x0C080000) → Secure(CMSE 代码)
  [0x0C080000 ~ Flash End)  → Non-Secure(应用代码)

MPC 配置 SRAM:
  [RAM_S_START ~ RAM_S_END) → Secure(安全世界数据)
  [RAM_NS_START ~ RAM_END)  → Non-Secure(应用数据)

3.8 Stage 8:Non-Secure World main()

源文件platform/main/main.cpp

NS 世界经历相同的 Boot_Loader → NVIC_InitVectors → BootInit 流程后,进入 main()

int main(void)
{
    // ═══ Phase 1:看门狗 ═══
    app_wdt_open(15);  // 15 秒超时

    // ═══ Phase 2:硬件 GPIO 配置 ═══
    tgt_hardware_setup();  // LED / 充电指示 GPIO

    // ═══ Phase 3:ROM 工具函数初始化 ═══
    rom_utils_init();

    // ═══ Phase 4:获取当前线程 ID ═══
    main_thread_tid = osThreadGetId();

    // ═══ Phase 5:硬件定时器初始化 ═══
    hwtimer_init();

    // ═══ Phase 6:DMA ═══
    hal_dma_set_delay_func((HAL_DMA_DELAY_FUNC)osDelay);
    hal_audma_open();
    hal_gpdma_open();

    // ═══ Phase 7:Flash API ═══
    norflash_api_init();

    // ═══ Phase 8:日志系统 ═══
    log_dump_init();
    custom_offline_log_init();
    crash_dump_init();

    // ═══ Phase 9:调试串口 ═══
    hal_iomux_set_uart0/1/2();
    hal_trace_open(HAL_TRACE_TRANSPORT_UARTx);
    hal_trace_set_log_level(TR_LEVEL_DEBUG);

    // ═══ Phase 10:ISPI 访问初始化 ═══
    hal_iomux_ispi_access_init();

    // ═══ Phase 11:Flash 大小校验 ═══
    uint32_t actualFlashSize = hal_norflash_get_flash_total_size(HAL_FLASH_ID_0);
    ASSERT(FLASH_SIZE <= actualFlashSize);

    // ═══ Phase 12:MPU 配置 ═══
    mpu_cfg();

    // ═══ Phase 13:PMU + 模拟外设 ═══
    pmu_open();
    analog_open();

    // ═══ Phase 14:随机数种子 ═══
    srand(hal_sys_timer_get());

    // ═══ Phase 15:工厂区读取 ═══
    factory_section_open();

    // ═══ Phase 16:应用初始化 ═══
    ret = app_init();  // BT / Audio / App 全部初始化

    // ═══ Phase 17:主循环(RTOS 事件等待)═══
    while(1) {
        evt = osSignalWait(0x0, osWaitForever);
        if (evt.value.signals & 0x04) break;  // 关机
        if (evt.value.signals & 0x08) break;  // 重启
    }

    // ═══ Phase 18:系统关机/重启 ═══
    app_deinit(ret);
    pmu_shutdown();  // 或 pmu_reboot()
}

Part 4:系统核心并发机制与 RTOS

本部分介绍 RTX5 实时操作系统的集成配置,以及系统中两种典型的并发保护机制——硬件中断锁与 RTOS 互斥量,阐明各自适用场景及关键差异。

4.1 RTX5 集成

4.1.1 RTX5 配置参数
// config/common.mk 中定义的 RTOS 参数:
KERNEL = RTX5                    // CMSIS-RTOS2 内核
OS_CLOCK_NOMINAL = 16000         // SysTick 频率 16 KHz(M33)
OS_TASKCNT = 12                  // 最大任务数(TWS 模式)
OS_SCHEDULERSTKSIZE = 768        // 调度器堆栈
OS_IDLESTKSIZE = 512             // 空闲任务堆栈
4.1.2 RTOS 关键中断
RTX5 使用的 ARM 异常:
┌──────────────────────────────────────────────────┐
│ SysTick(IRQ#15)                                │
│   频率:16 KHz(每 62.5 us 触发一次)            │
│   功能:时间片调度,osDelay 计时                  │
├──────────────────────────────────────────────────┤
│ PendSV(IRQ#14)                                 │
│   优先级:最低(0xFF)                           │
│   功能:上下文切换(任务调度器触发)              │
├──────────────────────────────────────────────────┤
│ SVC(IRQ#11)                                    │
│   功能:系统调用入口                             │
│   用途:osKernelStart、osThreadCreate 等          │
└──────────────────────────────────────────────────┘
4.1.3 任务切换流程
Current Task (TaskA)         PendSV Handler          New Task (TaskB)
      │                           │                        │
      │  osSignalSet/osDelay      │                        │
      │─────────────────────────► │                        │
      │                           │ 1. 保存 TaskA 上下文   │
      │                           │   (R4-R11, PSP, LR)  │
      │                           │                        │
      │                           │ 2. 选择最高优先级任务   │
      │                           │                        │
      │                           │ 3. 恢复 TaskB 上下文   │
      │                           │────────────────────────►│
      │                           │                        │ 继续执行

4.2 中断锁机制:int_lock_global vs osMutexWait

核心区分int_lock_global = 关闭 CPU 中断(硬件层面,纳秒级);osMutexWait = RTOS 互斥锁(软件层面,需要调度器参与)。

4.2.1 int_lock_global 原理

源文件platform/cmsis/inc/cmsis.h:56

__STATIC_FORCEINLINE uint32_t int_lock_global(void)
{
    uint32_t pri = __get_PRIMASK();   // 读当前 PRIMASK 寄存器(保存旧状态)
    if ((pri & 0x1) == 0) {           // 如果中断当前是开着的
        __disable_irq();              // 写 PRIMASK=1 → 关闭所有可屏蔽中断
    }
    return pri;                       // 返回旧状态(用于恢复)
}

__STATIC_FORCEINLINE void int_unlock_global(uint32_t pri)
{
    if ((pri & 0x1) == 0) {           // 加锁前中断是开的
        __enable_irq();               // 写 PRIMASK=0 → 恢复中断
    }
}

PRIMASK 寄存器是 ARM Cortex-M 的 1-bit 特殊寄存器:

  • PRIMASK = 0:允许所有可屏蔽中断(正常状态)

  • PRIMASK = 1屏蔽所有优先级的可屏蔽中断(NMI 和 HardFault 除外)

执行流程:
  Task A                        所有 IRQ
    │                              │
    │  uint32_t lock = int_lock_global()
    │  PRIMASK ← 1 ──────────────── X  (硬件门控,中断请求被 CPU 忽略)
    │
    │  <临界区,安全访问共享资源>
    │
    │  int_unlock_global(lock)
    │  PRIMASK ← 0 ──────────────── ✓  (立即恢复中断响应)

关键特性:

  • 单条汇编指令(CPSID I / CPSIE I),2 个 CPU 周期内完成

  • 不涉及 RTOS,在中断上下文、Boot 阶段、RTOS 启动前均可使用

  • 临界区必须极短(通常 < 1 μs)

  • 嵌套安全:返回值 pri 记录调用前状态,unlock 时只恢复原状态

4.2.2 int_lock(BASEPRI 模式)— INT_LOCK_EXCEPTION 变体

当编译宏 INT_LOCK_EXCEPTION 开启时,int_lock() 改用 BASEPRI 寄存器:

// BASEPRI 模式:只屏蔽优先级 >= IRQ_PRIORITY_HIGHPLUS(2) 的中断
// 允许 IRQ_PRIORITY_REALTIME(0) 和 IRQ_PRIORITY_HIGHPLUSPLUS(1) 继续响应
__set_BASEPRI(((IRQ_PRIORITY_HIGHPLUS << (8 - __NVIC_PRIO_BITS)) & 0xFFUL));

// BES1502P:__NVIC_PRIO_BITS = 3
// BASEPRI 写入值 = (2 << 5) = 0x40
// 效果:只有优先级 0(REALTIME) 和 1(HIGHPLUSPLUS) 的 IRQ 可以打断临界区
优先级对照(BES1502P,3 priority bits):
  0 = REALTIME       ← 不被屏蔽(最高优先级实时中断)
  1 = HIGHPLUSPLUS   ← 不被屏蔽
  2 = HIGHPLUS       ← 被屏蔽(BASEPRI 阈值)
  3 = HIGH           ← 被屏蔽
  4 = ABOVENORMAL    ← 被屏蔽
  5 = NORMAL         ← 被屏蔽
  6 = BELOWNORMAL    ← 被屏蔽
  7 = LOW            ← 被屏蔽(最低优先级)
4.2.3 osMutexWait 原理

源文件services/interconnection/umm_malloc/umm_malloc.c:70

void LOCK_UMM_CLOCK(void)
{
    if (osMutexWait(umm_clock_mutex_id, osWaitForever) != osOK)
        DBGLOG_INFO(1, "%s Error", __func__);
}

osMutexWait 是 CMSIS-RTOS v1 API(CMSIS-RTOS2 对应 osMutexAcquire),实现原理:

Task A 调用 osMutexWait(mutex, osWaitForever):
  │
  ├─ 互斥锁未被占用?
  │     YES → 原子地标记"被 Task A 持有",立即返回 osOK
  │     NO  → Task A 进入 BLOCKED 状态,加入等待队列
  │              │
  │              └─ RTX5 调度器切换到其他就绪任务
  │                 ...(Task B/C 运行中)...
  │                 Task X 调用 osMutexRelease(mutex)
  │                    │
  │                    └─ 唤醒等待队列中优先级最高的任务(Task A)
  │                       Task A 变为 READY,重新参与调度
  │                       下次 Task A 被调度时,从 osMutexWait 返回 osOK
4.2.4 核心差异对比
维度 int_lock_global osMutexWait
实现层 CPU 硬件寄存器(PRIMASK) RTOS 软件对象
速度 2 个 CPU 周期(~10 ns @208 MHz) 数百 CPU 周期(涉及调度器)
阻塞行为 不阻塞,立即关中断 可阻塞,锁忙时任务挂起
中断上下文 ✅ 可以使用 ❌ 不能在 ISR 中调用(触发 HardFault)
RTOS 启动前 ✅ Boot 阶段可用 ❌ 必须在 osKernelStart 后才有效
优先级反转 无(无调度概念) RTX5 内置优先级继承机制,防止反转
嵌套 ✅ 支持(靠返回值记录状态) ⚠️ RTX5 mutex 支持递归(需配置)
保护粒度 必须极短(影响中断实时性) 任意长度
适用场景 极短临界区、Boot/ISR 上下文 任务间共享资源(堆、链表、Flash API 等)
4.2.5 umm_malloc 为什么两种方式并存
// services/interconnection/umm_malloc/umm_malloc.c

#ifdef RS_BASE_LINE
// RS 基线版本:Boot 阶段或无 RTOS 场景,用中断锁
static uint32_t umm_lock = 0;
void LOCK_UMM_CLOCK(void)   { umm_lock = int_lock(); }
void UNLOCK_UMM_CLOCK(void) { int_unlock(umm_lock); }

#else
// 正常运行版本(有 RTOS):用互斥锁,支持任务间安全并发
void LOCK_UMM_CLOCK(void)   { osMutexWait(umm_clock_mutex_id, osWaitForever); }
void UNLOCK_UMM_CLOCK(void) { osMutexRelease(umm_clock_mutex_id); }
#endif
  • RS_BASE_LINE 场景下,RTOS 可能尚未启动,int_lock 是唯一安全选择

  • 正常运行时,堆操作可能耗时较长,用 Mutex 让其他任务不被"饿死"

4.2.6 使用决策树
需要保护共享资源?
        │
        ├─ 是否在 ISR / Boot 阶段 / RTOS 启动前?
        │     YES → 必须用 int_lock_global / int_lock
        │
        ├─ 临界区是否极短(< ~10 条指令)?
        │     YES → 用 int_lock(代价最小)
        │     NO  → 考虑 Mutex,避免屏蔽中断过久
        │
        └─ 是否跨任务共享(任一任务可能等待)?
              YES → 用 osMutexWait(任务可休眠让出 CPU)
              NO  → int_lock 足够

Part 5:存储布局与数据持久性策略

本部分从工程实践视角梳理 Flash 分区布局、供电域划分、RTC 独立运行机制,以及各类复位/关机场景下不同存储介质的数据生存性,为固件设计和数据可靠性保障提供参考。

5.1 Flash 存储结构

5.1.1 NOR Flash 配置
// norflash_cfg.h
struct norflash_cfg_struct_t {
    uint8_t neg_phase:1;   // 负相位
    uint8_t pos_neg:1;     // 正/负相位
    uint8_t samdly:3;      // 采样延时(0-7)
    uint8_t div;           // 时钟分频(最小 2)
    uint8_t dualmode:1;    // 双线模式
    uint8_t quadmode:1;    // 四线模式
    uint8_t mod_clk:4;     // 模块时钟选择
    uint8_t rdcmd;         // 读命令(0x03 慢速 / 0x0B 快速)
    uint8_t frdcmd;        // 快读命令(0x0B)
    uint8_t qrdcmd;        // 四线读命令(0xEB)
};
5.1.2 Flash 分区布局
┌──────────────────────────────────────────────────────┐
│ Flash Physical Layout                                │
├──────────────────────────────────────────────────────┤
│ 0x00000 ┌─────────────────────────────────────┐     │
│         │ Secure Boot Header (boot_struct_t)   │     │
│         │ + CMSE Firmware                      │     │
│ 0x80000 ├─────────────────────────────────────┤     │
│         │ NS Boot Header (boot_struct_t)       │     │
│         │ + Application Firmware               │     │
│         │ + BT Stack                           │     │
│         │ + Resources                          │     │
│         ├─────────────────────────────────────┤     │
│         ├─────────────────────────────────────┤     │
│         │ REGISTER_ENC_CRC(192 KB)           │     │
│         ├─────────────────────────────────────┤     │
│         │ IPU_ENC_CRC(192 KB)               │     │
│         ├─────────────────────────────────────┤     │
│         │ OTA Backup Area                     │     │
│         ├─────────────────────────────────────┤     │
│         │ Factory Section(NV Data)           │     │
│ End     └─────────────────────────────────────┘     │
└──────────────────────────────────────────────────────┘

5.2 关键数据结构

5.2.1 Boot Header Chain
// Flash 中的镜像布局:
//
// |<--- boot_struct_t --->|<--- code_sig_struct_t --->|<--- Code --->|
//
// boot_struct_t:
//   magic = 0xBE57EC1C
//   flag, version
//   security settings
//   RSA3072 public key(772 bytes)
//   RSA3072 signature(384 bytes)
//
// code_sig_struct_t:
//   code_size(被签名的代码大小)
//   code signature(384 bytes)
//
// 验签流程:
//   1. ROM 读取 boot_struct_t 中的 public key
//   2. 计算 code 区域的 SHA256 哈希
//   3. 用 RSA3072 验证 sig 是否匹配
//   4. 通过则允许执行,否则卡死
5.2.2 系统频率请求模型
// 多模块独立请求系统频率,取最大值
hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_104M);
hal_sysfreq_req(HAL_SYSFREQ_USER_RS_DOLBY_DECRYPTED, HAL_CMU_FREQ_208M);

// 模块完成工作后释放:
hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_32K);

// 系统实际频率 = max(所有模块请求)
// 32K 表示"我不需要高频",不是真的跑 32 KHz

5.3 供电域、RTC 与数据持久性

5.3.1 BES1502P 供电域全景

芯片内部存在多个完全独立的供电域,可在不同上电/断电状态下独立工作:

VBAT(电池 3.0~4.2 V)
  │
  ├─── VRTC 域(LDO_VRTC, 0.5~0.825 V)←─ 最小常供电域
  │      │
  │      ├─ PMU 核心逻辑(寄存器状态)
  │      ├─ RTC 计数器 + 日历模块
  │      ├─ PMU_REG_NOT_RESET_61 等"不复位寄存器"
  │      └─ 32K 时钟(LPO / 外部晶振)
  │
  ├─── AON 域(Always-On)
  │      │
  │      ├─ AON CMU(AONCMU_BASE = 0x40080000)
  │      │    └─ BOOTMODE 寄存器(0x38 偏移)
  │      └─ 部分 GPIO(唤醒用)
  │
  ├─── DCDC_DIG(数字核, 0.65~1.1 V)── CPU 关机后断电
  │      └─ ARM Cortex-M33 Core
  │         SRAM(RAM0~RAM9,约 2 MB)
  │         Flash 控制器 / PSRAM 控制器
  │
  ├─── DCDC_ANA(模拟核, 1.4~1.9 V)── CPU 关机后可断电
  │      └─ CODEC / PLL / RF / XTAL
  │
  └─── DCDC_HPPA(耳机功放, 1.4~1.9 V)── 按需开关
         └─ Headphone PA

关键结论

  • SRAM 属于 DCDC_DIG 域 → 断电/关机后全部清零

  • Flash 是独立的非易失性存储 → 任何复位/断电后数据不变

  • VRTC 域由 VBAT 直接维持 → 只要电池在,VRTC 域就存活

5.3.2 RTC:关机状态下独立运行的时钟

RTC 集成在 PMU 模拟域内,由 VRTC LDO 独立供电。

供电配置

// platform/drivers/ana/best1502p/reg_pmu_best1502p.h
PMU_REG_LDO_VRTC_CFG   = 0x129
PMU_REG_LDO_VRTC_VOLT  = 0x12A

// VRTC 电压范围:0.45 V ~ 0.825 V
// 默认活跃电压:PMU_LDO_VRTC_DEFAULT_ACT = 0.8 V
// 默认低功耗:  PMU_LDO_VRTC_DEFAULT_LP  = 0.65 V

RTC 寄存器

// PMU RTC 寄存器(通过 ISPI 访问)
PMU_REG_RTC_LOAD_LOW/HIGH    // 设置初始计数值
PMU_REG_RTC_VAL_LOW/HIGH     // 读取当前计数值
PMU_REG_RTC_DIV_1HZ          // 分频:32768 Hz → 1 Hz
PMU_REG_RTC_MATCH1_LOW/HIGH  // 匹配/唤醒比较值

// 日历格式寄存器(年月日时分秒)
PMU_REG_RTC_LOAD_MIN_SEC_EN  // 加载分/秒
PMU_REG_RTC_LOAD_HOURS       // 加载小时
PMU_REG_RTC_LOAD_DATAS_EN    // 加载日期
PMU_REG_RTC_LOAD_YEARS       // 加载年份
PMU_REG_RTC_GET_MIN_SEC      // 读取分/秒
PMU_REG_RTC_GET_HOURS        // 读取小时
PMU_REG_RTC_GET_DATAS        // 读取日期
PMU_REG_RTC_GET_YEARS        // 读取年份

RTC 时钟源(二选一,由 CLK_32K_SEL 配置):

┌─ LPO(Low Power Oscillator):片内 RC 振荡器
│   精度:~±3%,无需外部元件,功耗极低
│   适用:对时间精度要求不高的场景
│
└─ XO_32K:外部 32.768 KHz 晶振
    精度:±20 ppm(±1.7 秒/天)
    稳定时间:PMU_EXT_32K_STABLE_TIME = 300 ms
    适用:需要精确日历/闹钟的场景

关机后 RTC 的行为

pmu_shutdown() 执行后(SOFT_POWER_OFF=1):
  DCDC_DIG 断电 → CPU 停止 → SRAM 清零
  DCDC_ANA 断电 → PLL/RF/CODEC 停止
         │
         ▼
  VRTC 域仍然由 VBAT 维持:
    RTC 计数器继续计数  ✓  (32K 时钟仍运行)
    RTC 日历继续走时   ✓
    PMU 状态寄存器保持 ✓
    PMU_REG_NOT_RESET_61 保持 ✓
         │
         ├─ [场景 1] RTC 闹钟触发唤醒
         │    pmu_rtc_calendar_pwron_enable(true)  设置允许 RTC 开机
         │    → 到达设定时间 → PMU 触发开机
         │    → pmu_boot_cause_get() == PMU_BOOT_CAUSE_RTC
         │
         └─ [场景 2] 用户按电源键开机
              → pmu_boot_cause_get() == PMU_BOOT_CAUSE_POWER_KEY

代码示例

// 设置 RTC 日历时间
struct RTC_CALENDAR_FORMAT_T cal = {
    .year = 24, .month = 6, .day = 10,
    .hour = 8,  .min = 0,  .sec = 0,
};
pmu_rtc_calendar_set(&cal);

// 设置 10 分钟后 RTC 唤醒开机
struct RTC_CALENDAR_FORMAT_T alarm = {
    .hour = 8, .min = 10, .sec = 0, ...
};
pmu_rtc_calendar_alarm_set(&alarm);
pmu_rtc_calendar_pwron_enable(true);  // 允许 RTC 触发开机

// 执行关机(VRTC 域继续运行,CPU 停止)
pmu_shutdown();
// 10 分钟后 RTC 匹配 → PMU 自动上电 → 正常启动流程
// pmu_boot_cause_init() 中识别到 PMU_BOOT_CAUSE_RTC
5.3.3 各复位/关机场景下的数据存活性
复位/关机类型          │ SRAM │ AON_CMU.BOOTMODE │ PMU VRTC 寄存器 │ NOR Flash │ eFuse
───────────────────────┼──────┼──────────────────┼────────────────┼───────────┼──────
软件重启               │ 清零 │ 保持             │ 保持           │ 保持      │ 永久
(hal_cmu_sys_reboot)
───────────────────────┼──────┼──────────────────┼────────────────┼───────────┼──────
WDT 复位               │ 清零 │ 清零(注意!)   │ 保持           │ 保持      │ 永久
(pmu_wdt_reboot)
───────────────────────┼──────┼──────────────────┼────────────────┼───────────┼──────
软件关机               │ 清零 │ 可能保持/清零    │ 保持           │ 保持      │ 永久
(pmu_shutdown)
───────────────────────┼──────┼──────────────────┼────────────────┼───────────┼──────
Deep Sleep             │ 可配置│ 保持            │ 保持           │ 保持      │ 永久
(pmu_sleep)           │(保持)│                │                │           │
───────────────────────┼──────┼──────────────────┼────────────────┼───────────┼──────
电池彻底拔除           │ 清零 │ 清零             │ 清零           │ 保持      │ 永久
───────────────────────┴──────┴──────────────────┴────────────────┴───────────┴──────

关键实现细节

// pmu_sys_ctrl() 中复位前写 PMU 魔术字 → PMU 内部复位
pmu_write(PMU_REG_METAL_ID, 0xCAFE);
pmu_write(PMU_REG_METAL_ID, 0x5FEE);

// WDT 复位和 pmu_sys_ctrl() 最终都调用:
pmu_wdt_reboot();
// 注释:"Use pmu wdt reset instead of global reset, bootmode will be cleared"
// ↑ WDT 复位会清 BOOTMODE,而全局复位不会
// PMU_REG_NOT_RESET_61:VRTC 域内的"不被复位"寄存器,用于判断是否首次上电
pmu_read(PMU_REG_NOT_RESET_61, &val);
if ((val & REG_NOT_RESET_CHIP_PWR_ON) == 0) {
    pmu_boot_first_pwr_up = true;   // 真正的首次上电(电池刚插入)
}
val |= REG_NOT_RESET_CHIP_PWR_ON;
pmu_write(PMU_REG_NOT_RESET_61, val);
// 此位在 VRTC 掉电前(电池拔除)才会变回 0
// 软件重启/WDT/关机 都不影响它 → 所以叫 NOT_RESET
5.3.4 BOOTMODE 寄存器:跨重启的通信桥梁

AON_CMU.BOOTMODE(物理地址 0x40080038)存在于 AON 域,被软件复位时通常不会被自动清零:

┌─────────────────────────────────────────────────────────────┐
│  AON_CMU.BOOTMODE 寄存器(32-bit)                          │
│                                                             │
│  [3:0]  HW_BOOTMODE(硬件置位,标识复位原因)              │
│           bit0:watchdog 复位                               │
│           bit1:global 复位                                 │
│           bit2:RTC 唤醒                                    │
│           bit3:charger 插入                                │
│                                                             │
│  [31:4] SW_BOOTMODE(软件置位,跨重启传递意图)             │
│           bit4:  READ_ENABLED                              │
│           bit7:  FORCE_USB_DLD(强制进 USB 下载模式)      │
│           bit12:FACTORY(进入产测模式)                     │
│           bit16:REBOOT(主动软重启标记)                   │
│           bit19:POWER_DOWN_WAKEUP                          │
│           bit25:REBOOT_FROM_CRASH(崩溃重启标记)          │
└─────────────────────────────────────────────────────────────┘

用法示例:
  // 重启前标记"进入 OTA 模式"
  hal_sw_bootmode_set(HAL_SW_BOOTMODE_FORCE_USB_DLD);
  pmu_reboot();
  // 重启后读取
  uint32_t bm = hal_sw_bootmode_get();
  if (bm & HAL_SW_BOOTMODE_FORCE_USB_DLD) {
      // 进入 USB 下载模式
  }
5.3.5 Flash 各分区读写权限与持久性
偏移 分区 持久性 固件改写条件
0x00000 Secure Boot Header + CMSE 固件(512 KB) 永久 需签名验证,通常不改写
0x80000 NS App 固件 + BT Stack 永久 OTA 升级时改写,写前须先擦除
CUSTOM_(64 KB) 永久 专用烧录流程
REGISTER_ENC / IPU_ENC_CRC ENC 注册数据(各 192 KB) 永久 出厂烧录,不应被用户改写
OTA Backup OTA 备份区 永久 OTA 流程改写
Factory Section 产测/配对数据(NV Data) 永久 运行时可读写(见下方说明)

Factory Section — 运行时持久化存储

这是固件运行期间唯一会主动写 Flash 的用户数据区

// services/nv_section/factory_section/factory_section.c

// 写入:先擦扇区再写整页(4 KB sector)
norflash_api_erase(NORFLASH_API_MODULE_ID_FACTORY,
    (uint32_t)(__factory_start) & 0x00FFFFFF,
    FACTORY_SECTOR_SIZE, false);

norflash_api_write(NORFLASH_API_MODULE_ID_FACTORY,
    (uint32_t)(__factory_start) & 0x00FFFFFF,
    (uint8_t *)tmpBuf, FACTORY_SECTOR_SIZE, false);

存储内容:蓝牙配对记录(MAC + LinkKey)、音量/EQ 偏好设置、工厂校准数据、入耳检测阈值、产测标记位。这些数据跨任何复位/关机都能保持,只要不调用 Factory Reset(擦除)。

NV Record — 用户运行时数据

// services/nv_section/userdata_section/
// 存储:蓝牙配对历史、用户设置、ANC 配置、Fast Pair 账户密钥等
// 机制:类似日志追加,定期整理(wear leveling)
5.3.6 实际工程场景对照
场景 发生了什么 数据丢失 数据保留
代码崩溃 WDT 重启 数字域复位,SRAM 清零 堆/栈上所有临时数据 Flash 配对记录、用户设置、RTC 时间
长按电源键关机 SOFT_POWER_OFF,DCDC_DIG 断电 SRAM 全部 RTC 继续走时、Flash 所有数据
OTA 升级重启 先写 Flash 备份区,再 reboot 重启前 RAM 数据 新固件、旧配对记录、设置
电池拔除重插 VRTC 掉电,PMU 寄存器复位 SRAM、RTC 时间、BOOTMODE Flash 配对记录、用户设置、eFuse
工厂复位 主动擦除 Factory Section Factory Section 数据 固件本体、eFuse
固件烧录(全擦) 整片 Flash 擦除再写入 所有 Flash 数据(含配对) eFuse(物理熔丝,无法擦除)
5.3.7 Deep Sleep 时的 SRAM 数据保持

BES1502P 的 SRAM 在 Deep Sleep 模式下可配置为 Retention(数据保持) 模式:

// AON CMU RAM 配置寄存器
// platform/hal/best1502p/reg_aoncmu_best1502p.h
__IO uint32_t RAM_CFG;    // 0x5C - 控制每块 SRAM 的低功耗模式
__IO uint32_t RAM2_CFG0;  // 0x88
__IO uint32_t RAM2_CFG1;  // 0x8C
__IO uint32_t RAM3_CFG0;  // 0x90
__IO uint32_t RAM3_CFG1;  // 0x94

// 每块 SRAM 可独立选择:
//   Active:       正常读写
//   Light Sleep:  保持数据,降低功耗(几十 nA/KB 量级)
//   Deep Sleep:   保持数据,极低功耗(几 nA/KB 量级)
//   Power Down:   断电,数据丢失,功耗最低
Deep Sleep 时的存储层次:
  ┌────────────────────────────────────────────────┐
  │  电源状态           数据状态      功耗          │
  │  VRTC: ON       RTC 计数持续   < 1 μA          │
  │  DCDC_DIG: OFF─┐                               │
  │               ├─ Retention SRAM:数据保持 ✓    │
  │               │  (数十 KB 关键数据)~μA 量级   │
  │               └─ 其他 SRAM:断电,数据丢失      │
  │  Flash: OFF(不通电,数据天然保持)✓            │
  └────────────────────────────────────────────────┘

  唤醒后 CPU 恢复执行,Retention SRAM 中的数据
  (如 BT 连接状态、快速重连信息)可直接使用,
  无需重新从 Flash 读取 → 显著缩短唤醒延迟。

Part 6:工程参考手册与 FAQ

本部分提供源文件索引、编译配置摘要和常见问题解答,供日常开发查阅。

6.1 源文件索引

启动阶段 源文件 关键函数
ASM 入口 platform/main/startup_main.S Boot_Loader
向量表 platform/cmsis/cmsis_nvic.c NVIC_InitVectors
Boot 初始化 platform/cmsis/system_utils.c BootInit
时钟配置 platform/hal/hal_cmu_common.c hal_cmu_setup
PMU 驱动 platform/drivers/ana/best1502p/pmu_best1502p.c pmu_open
模拟驱动 platform/drivers/ana/best1502p/analog_best1502p.c analog_open
充电管理 platform/drivers/ana/best1502p/charger_best1502p.c charger_init
RF/XTAL platform/drivers/ana/best1502p/rf_xtal_best1502p.c rf_xtal_init
CMSE 主函数 platform/main/cmse_main.cpp MAIN_ENTRY
NS 主函数 platform/main/main.cpp main
Boot 结构体 utils/boot_struct/tool_msg.h boot_struct_t
Flash 配置 utils/boot_struct/norflash_cfg.h norflash_cfg_struct_t
内存映射 platform/hal/best1502p/plat_addr_map_best1502p.h 地址宏定义
编译配置 config/common.mk 芯片/RTOS/功能开关
链接脚本 scripts/link/tail_section.lds.S Flash 分区
RTOS 内核 rtos/rtx5/rtx_kernel.c osKernelStart
RTOS 线程 rtos/rtx5/rtx_thread.c 上下文切换

6.2 编译配置摘要

# config/common.mk(best1502p 关键配置):
CHIP = best1502p
CPU = m33                          # Cortex-M33
CHIP_HAS_FPU = 1                   # 硬件浮点
CHIP_HAS_USB = 1                   # USB OTG
CHIP_HAS_USBPHY = 1                # USB PHY
CHIP_HAS_PSRAM = 1                 # 外部 PSRAM
CHIP_HAS_CP = 1                    # Co-Processor
CHIP_HAS_PSC = 1                   # Power State Controller
CHIP_HAS_SECURE_BOOT = 1           # 安全引导
CHIP_FLASH_CTRL_VER = 7            # Flash 控制器 V7
CHIP_PSRAM_CTRL_VER = 8            # PSRAM 控制器 V8
CHIP_CACHE_VER = 5                 # Cache V5
CHIP_RAM_BOOT = 1                  # 支持 RAM Boot
PMU_IRQ_UNIFIED = 1                # PMU 中断统一处理
PMU_FORCE_LP_MODE = 1              # 强制低功耗模式
BOOT_LOADER_ENTRY_HOOK = 1         # Boot 入口 Hook
PROGRAMMER_BOOT_ENTRY_HOOK = 1     # 编程器 Boot Hook

# RTOS 配置:
RTOS = 1
KERNEL = RTX5
OS_CLOCK_NOMINAL = 16000           # 16 KHz tick
OS_TASKCNT = 12                    # TWS 模式最大任务数

6.3 常见问题解答(FAQ)

Q1:为什么有 Secure 和 Non-Secure 两个 main()?

ARM TrustZone 将 CPU 分为两个世界。Secure World(cmse_main.cpp)先启动,完成安全初始化和验签后,跳转到 Non-Secure World(main.cpp)运行实际应用。即使 NS 代码被篡改,S 世界的密钥和验签逻辑也不会泄露。


Q2:为什么要把代码从 Flash 搬到 RAM?

三个原因:

  1. 速度:Flash 读取有等待周期,SRAM 是零等待

  2. 启动依赖:Boot 阶段 Flash 控制器还未完全配置,某些代码必须从 RAM 执行

  3. 安全:NSC veneer 必须放在安全属性的 RAM 中


Q3:ISPI 是什么?

Internal SPI——芯片数字域与模拟域之间的内部通信总线。CPU 不能直接访问 PMU/RF 寄存器(它们在模拟域),必须通过 ISPI 发送读写命令。这就是为什么 hal_analogif_open() 必须在 pmu_boot_init() 之前调用。


Q4:RTX5 的任务在哪里创建的?

app_init() 中。BES SDK 使用 CMSIS-RTOS2 API:

osThreadNew(app_thread, NULL, &app_thread_attr);
osThreadNew(bt_thread,  NULL, &bt_thread_attr);
// 等等...

main() 本身运行在 RTX5 自动创建的一个线程中。


Q5:看门狗什么时候开始工作?

main() 的第一行就开启了 15 秒超时的看门狗。如果 Boot 过程卡住超过 15 秒,WDT 会自动复位芯片。正常工作后由各任务定期喂狗。


Q6:OTP 烧录后能读出来吗?

取决于保护设置。普通校准数据(DCDC 电压)可读;读保护的 page(如密钥哈希)一旦锁定,CPU 也读不出明文,只能被特定硬件加速器直接使用。


Q7:为什么 CMSE 阶段要"降频到 32K"再跳 NS?

hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_32K) 是"释放本模块的高频请求",不是真的把 CPU 降到 32 KHz。系统实际频率取所有模块请求的最大值。CMSE 完成工作后释放高频占用,让 NS 世界自己按需提频,避免浪费功耗。


Q8:int_lock 和 int_lock_global 有什么区别?

在不开 INT_LOCK_EXCEPTION 宏时两者完全相同(int_lock 直接调用 int_lock_global)。开启 INT_LOCK_EXCEPTION 后:

  • int_lock_global 始终操作 PRIMASK(关闭所有可屏蔽中断)

  • int_lock 改用 BASEPRI(只屏蔽优先级 ≥ HIGHPLUS 的中断,保留 REALTIME/HIGHPLUSPLUS 级别的实时响应)

BES1502P 工程中 INT_LOCK_EXCEPTION 的使能状态决定了实际行为,需查看 config/best1502p/target.mk


*芯片:BES1502P (BES2710Y) \

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐