杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • SDK 总览与芯片能力
    • 环境搭建与编译构建
    • 烧录与固件升级
    • 文档与版本资源
  • 应用与示例方案

    • demo 示例工程
    • WiFi 摄像头方案 (wifi_camera)
    • WiFi 音箱方案 (wifi_soundbox)
    • WiFi 婴儿监护方案 (wifi_bbm)
    • 公共应用模块库
    • 示例代码库 (example)
  • 系统架构与平台

    • 总体架构与工程分层
    • 系统启动与运行框架
    • 芯片驱动与板级适配
    • 设备管理与文件系统
    • 系统工具库与算法
  • 音频子系统

    • 音频框架与处理节点
    • 音频编解码与音效
    • 播放器与录音器
    • 语音交互与 AI 唤醒
    • LE Audio 与蓝牙音频
    • 音频调试与歌词
  • 视频与显示子系统

    • 摄像头驱动与 ISP
    • 视频编码与图像处理
    • 显示与 GPU 加速
    • 屏幕镜像 (screen_mirror)
  • 无线连接与网络

    • 蓝牙协议栈 (双模蓝牙)
    • WiFi 协议栈与配网
    • 网络协议栈
    • 云平台与 IoT 协议
  • UI 子系统

    • LVGL 集成与应用
    • UI 工程与工具链
  • 配置系统

    • 功能配置
    • 板级配置
    • 网络与蓝牙配置
    • 音频配置与提示音
  • 工具与测试

    • 产测与射频测试工具
    • 固件升级与更新机制
    • 调试与日志工具
  • 硬件参考设计

    • 原理图参考设计
    • 芯片数据手册

系统启动与运行框架

AC792N 系列 SDK 的系统启动与运行框架负责从芯片复位到应用层 app_main() 执行的完整引导链,包括双核(CPU0/CPU1)启动、分级 initcall 初始化机制、系统定时器与性能计数器、应用任务与消息循环,以及 C++ 运行时支撑。它是所有 AC792N 应用(如 wifi_bbm、wifi_camera、wifi_soundbox)共用的基础运行环境。

Purpose and Scope

本页面深入解析 SDK 中所有应用共用的启动与运行时基础设施:

  • 启动入口与双核引导流程(cpu1_main、app_task_handler)
  • 分级 initcall 机制(early_initcall / platform_initcall / initcall / module_initcall / late_initcall)
  • 应用任务与消息循环模型(os_task_pend("taskq", ...))
  • 运行时支撑设施:errno/jiffies/sysconf、C++ ABI 桩(__cxa_*)、标准库桩函数
  • 调试与保护机制:RTOS 栈检查、内存泄漏检测、dlog 日志、norflash 关键区域擦写保护

以下主题属于其他页面,不在本页展开:具体外设驱动初始化(asm/includes.h 中的平台外设)、各应用自身的业务逻辑(app_main 的具体实现)、RTOS 内核调度细节。

Overview

在 AC792N SDK 中,系统启动被设计为**"极简引导 + 分级初始化 + 独立应用任务"**三层结构:

  1. 极简引导:cpu1_main() 仅完成中断、调试与 RTOS 内核启动,保证系统以最小代价进入多任务环境;
  2. 分级初始化:app_task_handler() 在一个专用任务中按依赖顺序执行 5 级 initcall,从板级早期初始化(board_early_init)逐步推进到平台初始化、模块初始化,最后是应用核心初始化(app_core_init)与 app_main();
  3. 独立运行:app_main() 返回后,任务进入 os_task_pend("taskq", msg, ...) 消息循环,由事件驱动应用运行。

这种设计意图(WHY):外设与模块之间存在严格依赖关系(例如 norflash 关键区域保护必须等 VM 初始化完成后再注册),分层 initcall 让模块以"声明式"方式注册自己的初始化函数,由框架保证顺序,避免在每个应用中手工维护初始化清单。同时,将应用主逻辑放在独立任务中,使其不阻塞系统级初始化,并天然获得消息队列的并发安全模型。

关键概念:

概念说明
initcall通过链接段(section)收集的初始化函数注册机制,按级别分组执行
app_task_handler系统启动任务入口,串行执行全部初始化并承载应用主循环
taskq应用任务挂起的消息队列,驱动事件处理
perf_counter性能计数器,提供 delay_us/delay_ms/timer_get_ms 等时间接口
cpu1_run_flag标记 CPU1 是否完成启动,供 CPU0 同步使用

Architecture

flowchart TD
    subgraph sg_CPU0["CPU0 主核"]
        MainEntry["main() / 芯片复位入口"]
        MainEntry -->|"唤醒 CPU1"| CPUSync["cpu1_run_flag 同步"]
    end

    subgraph sg_CPU1["CPU1 从核(app 运行核)"]
        CPU1Main["cpu1_main()"]
        CPU1Main -->|"开启分支预测"| IRQInit["interrupt_init()"]
        IRQInit --> DebugInit["debug_init()"]
        DebugInit -->|"CPU_CORE_NUM > 1"| OSStart["os_start() RTOS 内核启动"]
        OSStart --> AppTask["app_task_handler() 系统启动任务"]
    end

    subgraph sg_Init["分级初始化链"]
        AppTask --> PerfInit["perf_counter_init()"]
        PerfInit --> SysTimer["sys_timer_init() / sys_timer_task_init()"]
        SysTimer --> EarlyInit["__do_initcall(early_initcall)"]
        EarlyInit --> BoardEarly["board_early_init()"]
        BoardEarly --> PlatformInit["__do_initcall(platform_initcall)"]
        PlatformInit --> FlashProtect["norflash 关键区域保护注册"]
        FlashProtect --> BoardInit["board_init()"]
        BoardInit --> DlogInit["dlog 输出设备初始化"]
        DlogInit --> Initcall["__do_initcall(initcall)"]
        Initcall --> ModuleInit["__do_initcall(module_initcall)"]
        ModuleInit --> AppCore["app_core_init()"]
        AppCore --> LateInit["__do_initcall(late_initcall)"]
        LateInit --> AppMain["app_main() 应用入口"]
        AppMain --> MsgLoop["while(1) os_task_pend(taskq)"]
    end

    subgraph sg_Debug["调试与保护设施"]
        StackCheck["rtos_stack_check_func(60s 周期)"]
        MemLeak["malloc_debug_dump(60s 周期)"]
        CppRuntime["cpp_run_init()(C++ 运行时)"]
        Finsh["finsh_system_init()(命令行调试)"]
    end

    AppTask --> CppRuntime
    AppTask --> StackCheck
    AppTask --> MemLeak
    AppMain --> Finsh

架构说明:CPU0 负责最底层的硬件引导与唤醒 CPU1;CPU1 完成中断/调试/RTOS 初始化后创建系统启动任务 app_task_handler,该任务按依赖顺序推进 5 级 initcall、板级回调与调试设施,最终调用 app_main() 并进入 taskq 消息循环。调试设施(栈检查、内存泄漏检测、dlog、finsh)以"周期定时器回调 + 初始化钩子"的方式挂接到框架中,与业务逻辑解耦。

来源:init.c、init.c

启动入口与双核引导

CPU1 从核入口:cpu1_main

cpu1_main() 是所有 AC792N 应用在 CPU1 上的第一段 C 代码,其职责被刻意保持最小化——只做四件事:开启分支预测、初始化中断、初始化调试、启动 RTOS 内核:

void cpu1_main(void)
{
    q32DSP(1)->PMU_CON1 &= ~BIT(8);     //cpu1开启分支预测功能

    __local_irq_disable();

    interrupt_init();

    debug_init();

#if CPU_CORE_NUM > 1
    cpu1_run_flag = 1;	//标记CPU1启动,不能挪到位置,否则会出现擦写flash卡死
    os_start();
#else
    puts("\r\n\n cpu1_run... \r\n\n");
    cpu1_run_flag = 1;	//标记CPU1启动,不能挪到位置,否则会出现擦写flash卡死
#endif

    __local_irq_enable();

#if defined CONFIG_UCOS_ENABLE ||  (CPU_CORE_NUM == 1)
    //在这句话之后 不可用操作系统接口,printf/puts等打印接口,并且需要替换单核专用的system库,
    while (1) {
        __asm__ volatile("idle");
    }
#endif
}

来源:init.c

设计要点:

  • 分支预测:q32DSP(1)->PMU_CON1 &= ~BIT(8) 在进入任何复杂逻辑前开启 CPU1 的分支预测,提升整体执行效率;
  • cpu1_run_flag 的放置位置是强约束:源码注释明确"不能挪到位置,否则会出现擦写 flash 卡死"。该标志必须在内核启动(os_start())之前置位,CPU0 依赖它判断 CPU1 是否已就绪,从而避免在 flash 操作期间出现双核竞争;
  • 单核模式:当 CPU_CORE_NUM == 1 或启用 uC/OS 时,CPU1 完成标记后直接进入 idle 指令空转循环——因为此时不可再使用操作系统接口,必须切换到单核专用的 system 库;
  • cpu_assert_debug():断言失败路径会先冲刷 dlog 到 flash、冲刷日志,然后 cpu_reset() 复位——保证现场信息落盘后再重启,便于离线分析。

系统启动任务:app_task_handler

cpu1_main 只负责内核启动;真正完整的系统初始化由 RTOS 创建的任务 app_task_handler 执行。其初始化序列是框架的核心,严格遵循"时间接口 → 系统定时器 → 早期初始化 → 板级 → 平台 → 保护 → 模块 → 应用"的依赖顺序:

static void app_task_handler(void *p)
{
#ifdef CONFIG_CXX_SUPPORT
    void cpp_run_init(void);
    cpp_run_init(); //使用c++时,必须先调用该接口进行初始化
#endif

    perf_counter_init();//初始化后才可使用 delay_us delay_ms timer_get_ms 等接口
    sys_timer_init();
    sys_timer_task_init();

#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
    sys_timer_add(NULL, rtos_stack_check_func, 60 * 1000);
#endif

#ifdef CONFIG_MEM_LEAK_CHECK_ENABLE
    malloc_debug_start();
    sys_timer_add(NULL, malloc_debug_dump, 60 * 1000);
#endif

    __do_initcall(early_initcall);
    board_early_init();
#ifdef CONFIG_SDFILE_EXT_ENABLE
    sdfile_ext_mount_init();
#endif
    __do_initcall(platform_initcall);

    // norflash关键区域添加擦写保护,需要放到vm初始化后进行
    norflash_key_addr_info_init();
    norflash_protect_opt_register(norflash_protect_opt);
    puts("flash core area protect init\n");

    board_init();
    ...
    __do_initcall(initcall);
    __do_initcall(module_initcall);
    app_core_init();
    __do_initcall(late_initcall);

    app_main();
    ...
    while (1) {
        res = os_task_pend("taskq", msg, ARRAY_SIZE(msg));
        ...
    }
}

来源:init.c

关键顺序语义(WHY):

  1. cpp_run_init() 必须最先:一旦启用 CONFIG_CXX_SUPPORT,C++ 的全局构造/析构需要运行时支撑,必须先于任何 C++ 对象使用;
  2. perf_counter_init() 先于定时器:sys_timer_init 依赖性能计数器提供的时间基准;初始化完成后才能安全使用 delay_us/delay_ms/timer_get_ms;
  3. norflash 保护在 platform_initcall 之后:源码注释明确指出"需要放到 vm 初始化后进行",因为关键区域地址信息依赖 VM 已挂载 flash 分区;
  4. app_core_init() 在 module_initcall 与 late_initcall 之间:应用核心(如消息中心、事件系统)就绪后再执行最晚一级的初始化,保证依赖应用核心的模块可用;
  5. app_main() 是应用真正入口,由各应用(wifi_bbm/wifi_camera/wifi_soundbox 等)分别实现,框架通过 extern void app_main(void); 声明引用,实现"框架-应用"的编译期绑定。

initcall 分级机制

框架通过 __do_initcall(level) 宏按级别批量执行注册的初始化函数。级别从早到晚依次为:

级别执行时机典型用途
early_initcall板级早期初始化前最基础的硬件资源、时钟、内存段
platform_initcallboard_early_init 后、norflash 保护前平台外设、存储、VM 等
initcall应用核心初始化前常规模块驱动
module_initcallapp_core_init() 前依赖应用核心框架的模块
late_initcallapp_core_init() 后最后收尾、依赖完整运行环境的初始化

这种"声明式注册 + 框架统一执行"的机制让新增模块无需修改启动文件:模块只需在自己的源文件中用对应宏注册初始化函数,链接器将其放入专属段,__do_initcall 按段顺序统一调用。这是嵌入式 Linux 内核 initcall 思想的精简移植,也是本框架可扩展性的核心。

核心启动流程

sequenceDiagram
    participant ROM as 芯片复位/引导
    participant CPU0 as CPU0 主核
    participant CPU1 as CPU1 从核
    participant RTOS as RTOS 内核
    participant APP as app_task_handler
    participant DEV as 外设/模块

    ROM->>CPU0: 复位向量进入 main
    CPU0->>CPU1: 唤醒并启动 cpu1_main
    CPU1->>CPU1: 开启分支预测/关中断
    CPU1->>CPU1: interrupt_init() + debug_init()
    CPU1->>CPU0: cpu1_run_flag = 1
    CPU1->>RTOS: os_start()
    RTOS->>APP: 创建系统启动任务
    APP->>APP: cpp_run_init()(若启用 C++)
    APP->>APP: perf_counter_init() + sys_timer_init()
    APP->>DEV: __do_initcall(early_initcall)
    APP->>DEV: board_early_init()
    APP->>DEV: __do_initcall(platform_initcall)
    APP->>DEV: norflash 关键区域保护注册
    APP->>DEV: board_init() + dlog 输出初始化
    APP->>DEV: __do_initcall(initcall)
    APP->>DEV: __do_initcall(module_initcall)
    APP->>APP: app_core_init()
    APP->>DEV: __do_initcall(late_initcall)
    APP->>APP: app_main()(应用业务初始化)
    APP->>APP: finsh_system_init()(可选)
    APP->>APP: while(1) os_task_pend("taskq", msg)
    Note over APP: 进入事件驱动消息循环,应用生命周期开始

时序要点:CPU1 在 os_start() 前通过 cpu1_run_flag 与 CPU0 完成握手;app_task_handler 在 RTOS 中作为一个普通任务运行,其初始化全部串行完成,最后驻留于 taskq 消息队列等待事件。该队列由 os_task_pend 挂起,os_task_post 投递,构成应用层事件驱动的统一入口。

运行时支撑设施

errno / jiffies / sysconf

框架在系统层提供了 POSIX 风格的运行时基础:全局 errno 与 __errno() 访问器、全局节拍计数 jiffies、以及 sysconf 的处理器数量查询:

long sysconf(int name)
{
    if (name == _SC_NPROCESSORS_CONF) {
        return CPU_CORE_NUM;
    } else {
        printf("[%s, %d][%d]is not supported yet\n", __FUNCTION__, __LINE__, name);
    }
    return -1;
}

int errno;
__attribute__((used)) int *__errno(void)
{
    return &errno;
}

volatile unsigned long jiffies = 10;

来源:init.c

设计意图:让依赖 libc/POSIX 的第三方代码(如文件系统、网络栈)能在无完整操作系统支持的 MCU 环境下编译链接并正确运行。__errno 使用 __attribute__((used)) 防止链接器裁剪,确保 errno 符号始终可见。

C++ 运行时 ABI 支撑(init_expand.c)

init_expand.c 集中实现了 C++ 运行时与标准库的"最小可用"支撑,包括纯虚函数/删除虚函数陷阱、atexit 注册、以及常用标准库函数的桩实现:

__attribute__((noreturn))
void __cxa_pure_virtual(void)
{
    printf("Need to make sure \"__cxa_pure_virtual\" runs OK!");
    while (1);
}

int __cxa_atexit(void (*destructor)(void *), void *arg, void *dso)
{
    // 调用这个函数来注册全局变量的析构函数
    // 析构全局变量的时候,会调用这里注册的东西
    return 0;
}

来源:init_expand.c

__cxa_pure_virtual 与 __cxa_deleted_virtual 均为 noreturn 陷阱:一旦调用到纯虚/删除虚函数即打印提示并死循环,把"未定义行为"变成可诊断的明确故障。__cxa_atexit 是全局析构注册的桩——由于嵌入式系统通常不复位销毁全局对象,直接返回 0 即可。swprintf/fgets/fflush/fprintf/vasprintf 等桩函数则保证依赖这些符号的库可以链接通过,其中 fprintf 被重定向到 vprintf(即串口打印),fflush 为空操作——符合无缓冲 printf 的 MCU 语义。

使用示例

示例 1:注册 norflash 关键区域擦写保护

框架提供"注册回调 + 判定钩子"的保护模式:任何模块想保护 flash 关键地址,只需实现判定函数并通过 norflash_protect_opt_register 注册;之后每次擦写操作都会经过 norflash_protect_opt 检查:

extern void norflash_protect_opt_register(int (*protect_opt_func)(u32, u32));
extern int norflash_key_addr_info_init(void);
extern int norflash_key_addr_judge(u32 opt_addr, u32 opt_len);
int norflash_protect_opt(u32 opt_addr, u32 opt_len)
{
    if (norflash_key_addr_judge(opt_addr, opt_len)) {
        ASSERT(0, "you are operating norflash key addr!!");
        // cpu_reset();
        // return -1; //如果只希望跳过操作,不重启,返回-1
    }
    return 0;
}

来源:init.c

用法说明:在 app_task_handler 中,框架先调用 norflash_key_addr_info_init() 加载关键地址表,再 norflash_protect_opt_register(norflash_protect_opt) 注册钩子(必须位于 VM 初始化之后,见 init.c L246-L248)。命中关键地址时默认 ASSERT(0) 停机;如需"跳过操作继续运行",可取消注释 return -1 分支。

示例 2:周期任务挂接(栈检查 / 内存泄漏检测)

框架通过 sys_timer_add 将调试任务注册为周期回调,无需修改启动流程即可启用运行时诊断:

#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
static void rtos_stack_check_func(void *p)
{
#if defined CONFIG_UCOS_ENABLE
    void task_info_output(int size);
    task_info_output(0);

    int usage[CPU_CORE_NUM] = {0};
    os_cpu_usage(NULL, usage);

    for (int i = 0; i < CPU_CORE_NUM; ++i) {
        printf("cpu%d use: %d%%\n", i, usage[i]);
    }
#endif

    get_task_state(); //1分钟以内调用一次才准确
    dump_cpu_irq_usage();
    /* dump_os_sw_cnt(); */
    malloc_stats();

    extern void efficiency_calculate_show(void);
    efficiency_calculate_show();
}
#endif

来源:init.c

注册方式(60 秒周期):

#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
    sys_timer_add(NULL, rtos_stack_check_func, 60 * 1000);
#endif

来源:init.c

rtos_stack_check_func 输出每个任务栈使用率、CPU 占用率、IRQ 占用统计与堆内存统计;注释"1 分钟以内调用一次才准确"提示 get_task_state 的采样周期约束——这是周期设为 60s 的原因。

示例 3:弱符号板级回调扩展

框架将板级钩子声明为 weak 符号,允许各应用选择性覆盖而无需修改框架源码:

void __attribute__((weak)) board_init() {}
void __attribute__((weak)) board_early_init() {}

来源:init.c

设计意图:board_early_init 在 early_initcall 之后、platform_initcall 之前调用,适合极早期的板级资源(如电源、时钟树)配置;board_init 在 norflash 保护注册之后、initcall 之前调用,适合依赖存储/VM 的板级外设初始化。若应用未实现,weak 空实现保证链接通过——这是"默认可用、按需覆盖"的扩展模式。

配置选项

框架行为由编译期宏控制(均定义于各应用的 app_config.h / 编译选项中):

选项类型默认说明
CPU_CORE_NUMint1/2内核数;> 1 时 CPU1 执行 os_start() 进入多核模式,否则空转
CONFIG_UCOS_ENABLEbool关启用 uC/OS;启用后 CPU1 启动完成后进入 idle 空转
CONFIG_CXX_SUPPORTbool关启用 C++ 运行时,启动任务最先调用 cpp_run_init()
CONFIG_RTOS_STACK_CHECK_ENABLEbool关启用栈检查,注册 60s 周期 rtos_stack_check_func
CONFIG_MEM_LEAK_CHECK_ENABLEbool关启用内存泄漏检测,注册 60s 周期 malloc_debug_dump
CONFIG_SDFILE_EXT_ENABLEbool关启用扩展文件系统挂载(sdfile_ext_mount_init)
CONFIG_DEBUG_ENABLEbool开断言失败时冲刷日志后复位(cpu_assert_debug)
CONFIG_FINSH_ENABLEbool关启用 finsh 命令行,app_main 后执行 finsh_system_init()
TCFG_DEBUG_DLOG_ENABLEbool关启用 dlog 日志系统(flash/UART 双输出)
TCFG_DLOG_OUTPUT_TYPEenum-dlog 输出类型(flash 缓存 / UART)
TCFG_RF_FCC_TEST_ENABLEbool关FCC 测试模式;命中后初始化失败则死循环等待
TCFG_RF_PRODUCT_TEST_ENABLEbool关产测模式,同 FCC 分支处理
TCFG_EXT_RF_FCC_TEST_ENABLEbool关外部 FCC 工具初始化分支
CONFIG_DEMO_UI_PROJECT_ENABLEbool关AWTK UI 工程;启用时跳过 cpp_run_init 以避免卡顿

API 参考

cpu1_main(void): void

CPU1 从核入口。开启分支预测、初始化中断与调试、置位 cpu1_run_flag、启动 RTOS 内核或进入单核空转。由引导代码直接调用,应用层不应调用。

cpu_assert_debug(void): void

断言失败处理:冲刷 dlog 到 flash(若启用)、冲刷日志、关中断并 cpu_reset() 复位。保证崩溃现场可追溯。

cpu_assert(char *file, int line, bool condition, char *cond_str): void

断言接口(当前为空实现,供编译期引用)。

app_task_handler(void *p): void

系统启动任务。串行执行 C++ 运行时、性能计数器、系统定时器、5 级 initcall、板级回调、norflash 保护、dlog 初始化、FCC/产测分支,最后调用 app_main() 并驻留 taskq 消息循环。

__do_initcall(level): void

执行指定级别(early_initcall/platform_initcall/initcall/module_initcall/late_initcall)下所有注册的初始化函数。

norflash_protect_opt(u32 opt_addr, u32 opt_len): int

norflash 擦写保护回调:命中关键地址则 ASSERT(0)(可改为 return -1 跳过操作)。返回值 0 表示允许操作。

norflash_protect_opt_register(int (*protect_opt_func)(u32, u32)): void

注册 norflash 保护回调,框架在每次擦写操作前调用。

sysconf(int name): long

POSIX 系统配置查询,当前仅支持 _SC_NPROCESSORS_CONF(返回 CPU_CORE_NUM),其余参数打印"not supported yet"并返回 -1。

__cxa_pure_virtual(void): void / __cxa_deleted_virtual(void): void

C++ 纯虚/删除虚函数陷阱,noreturn,打印提示后死循环。

__cxa_atexit(void (*destructor)(void *), void *arg, void *dso): int

C++ 全局析构注册桩,恒返回 0(嵌入式场景不复位销毁全局对象)。

失败模式、边界情况与并发

启动期失败

  • FCC/产测初始化失败:当 TCFG_RF_FCC_TEST_ENABLE || TCFG_RF_PRODUCT_TEST_ENABLE 且 rf_fcc_test_init() 返回非 0 时,启动任务进入 while(1) os_time_dly(10) 死循环——系统停在测试模式而非继续正常启动,这是刻意的"测试优先"设计,防止未通过 RF 校准的设备进入业务运行。
  • C++ 纯虚/删除虚函数调用:触发 __cxa_pure_virtual/__cxa_deleted_virtual 后打印提示并死循环。虽然不如异常优雅,但在无 MMU/异常处理的 MCU 环境中,死循环+日志是唯一可诊断的失败表达。
  • CONFIG_DEMO_UI_PROJECT_ENABLE(AWTK):框架主动跳过 cpp_run_init()。源码注释说明 AWTK 的第三方库 agge 初始化耗时极长("waitting 50s"),跳过以规避启动卡顿——这是对第三方 UI 库性能问题的框架级妥协。

并发与同步

  • 双核握手:cpu1_run_flag 是 CPU0/CPU1 之间的启动同步点。源码明确禁止移动其置位位置,否则"会出现擦写 flash 卡死"——因为 CPU0 可能在 CPU1 尚未完成中断/调试初始化时就发起 flash 操作,导致总线竞争。这属于严格时序约束的共享标志,非原子操作也安全,因为置位发生在 RTOS 启动前的单核上下文。
  • 初始化全部串行:5 级 initcall 在同一任务内顺序执行,天然无并发问题;这保证了初始化期间不存在多任务竞争,代价是启动耗时线性累加。
  • 消息循环单消费者:os_task_pend("taskq", msg, ...) 由单个应用任务消费,事件按队列顺序处理,避免应用层自建锁。

边界情况

  • 单核模式(CPU_CORE_NUM == 1):cpu1_main 不启动 RTOS,置位标志后直接 idle 空转,且注释要求"替换单核专用的 system 库"——多核代码若在单核构建下运行,须确认所链接 system 库与配置匹配。
  • 栈检查采样周期:get_task_state 的统计在 1 分钟以内调用一次才准确,因此周期固定为 60s;缩短周期会导致 CPU 占用统计失真。
  • norflash 保护注册时机:必须晚于 VM 初始化(platform_initcall 之后),否则关键地址表无法从已挂载分区加载。

性能与运维

  • 启动路径热点:启动时间主要由 initcall 总量、cpp_run_init(若启用 C++)与 dlog flash 冲刷(dlog_flush_all_cache_and_clear(-1) 为无限等待)决定。调试期可临时关闭 TCFG_DEBUG_DLOG_ENABLE 以缩短启动时间。
  • 周期诊断任务开销:栈检查与内存泄漏检测均为 60s 周期,且 malloc_stats、dump_cpu_irq_usage 等打印集中在回调内,对运行期性能影响可忽略;但高频打印会占用 UART 带宽,量产版本应通过配置宏关闭。
  • 调试日志双通道:dlog 支持 UART 与 flash 双输出;dlog_flush_all_cache_and_clear 在启动期将缓存写入 flash,保证早期日志不丢失。cpu_assert_debug 在复位前再次冲刷,形成"崩溃现场落盘"的运维闭环。
  • 堆监控:malloc_debug_start + malloc_debug_dump 提供堆泄漏追踪能力,配合 malloc_dump 可在长稳测试中定位泄漏点。

扩展点

  1. 新增 initcall 模块:在任意源文件中用 early_initcall/platform_initcall/initcall/module_initcall/late_initcall 宏注册初始化函数,框架自动按序调用——这是最常见的扩展方式,新增驱动/模块无需改动启动文件。
  2. 弱符号板级钩子:实现 board_early_init() / board_init() 覆盖 weak 空实现,插入板级定制初始化。
  3. 周期任务注册:复用 sys_timer_add 挂接自定义周期任务(与栈检查/内存泄漏检测同机制)。
  4. norflash 保护钩子:通过 norflash_protect_opt_register 注入自定义擦写保护策略(如整片保护、按地址区间保护)。
  5. finsh 命令行:启用 CONFIG_FINSH_ENABLE 后可在 app_main 之后进入交互式调试环境,用于运行期探查。

测试与验证

仓库中未发现针对系统启动框架的独立单元测试文件;该框架的验证主要依赖:

  • 编译期开关矩阵:CPU_CORE_NUM、CONFIG_UCOS_ENABLE、CONFIG_CXX_SUPPORT 等宏的不同组合构成各应用的实际构建配置,框架须在所有组合下可链接(weak 符号与 __cxa_* 桩正是为此服务);
  • 运行时诊断输出:rtos_stack_check_func 的栈/CPU 使用率、malloc_stats 堆信息、efficiency_calculate_show 效率统计构成启动与长稳测试的观测面;
  • 断言与保护:cpu_assert_debug 的复位前日志冲刷与 norflash 关键地址保护,为故障注入测试提供确定性行为。

Related Links

  • 系统任务模型(task.h / os_task_pend)——taskq 消息队列的接口定义
  • init.h——initcall 宏与 __do_initcall 声明
  • app_core.h——app_core_init() 应用核心框架
  • init_expand.c——C++ ABI 与标准库桩实现
  • cpp_run_init.c——C++ 运行时初始化入口
  • 各应用入口 app_main() 实现(如 sdk/apps/wifi_bbm/、sdk/apps/wifi_camera/、sdk/apps/wifi_soundbox/)——应用层业务,属于各自应用页面范围
Prev
总体架构与工程分层
Next
芯片驱动与板级适配