XVF3800 默认核资源使用分析(上): 16 个逻辑核,一个都没闲着
给 XVF3800 加功能(UART、自定义协议、常驻算法)之前,必须先回答一个问题:芯片还有没有核可用?本文对 v3.2.1 默认源码(main_tile0.c / main_tile1.c 的 PAR_JOBS 结构)与官方预编译的 23 个 .xe 固件逐一做 xobjdump 实测,把 16 个逻辑核、每 tile 512 KB SRAM 的账算清楚。
一句话结论:两个 tile 的 8 个逻辑核全部占满(8/8),默认固件里没有任何空闲核;UA 构建下
tile[1]的 SRAM 只剩约 4.2 KB。要加常驻逻辑,只能复用reserved_core预留核,或挤占现有核的帧内空闲时间——不能「再开一个核」。
一、结论总览
| 项目 | tile[0] | tile[1] |
|---|---|---|
| 静态核需求 | 8 / 8,占满 | 8 / 8,占满 |
| 主要占用 | SHF 后处理 3 核 + 麦克风 1 核 + USB 2 核 + FreeRTOS 2 核 | SHF 回声消除 4 核 + 音频路由 1 核 + I2S 1 核 + 预留核 1 核 + FreeRTOS 1 核 |
| UA 构建剩余 SRAM | 约 9.8 KB | 约 4.2 KB(非常紧张) |
| INT 构建剩余 SRAM | 约 33.5 KB | 约 32.4 KB |
工程含义:默认固件没有任何空闲逻辑核。若要在 XVF3800 上追加功能,只能复用 reserved_core 预留核,或挤占现有核的帧内空闲时间,而不能「再开一个核」。
另一个重要发现:.xe 内每个 tile 有两个 Loadable(引导桩 + 主应用)。xobjdump --resources 汇总中 tile[0] 那几行描述的是主应用(可信);tile[1] 那 5 行(program 6104 / thread 1 …)描述的是引导桩而非主应用——它在每个固件里完全相同,所以 23 个固件数值恒定。用 xobjdump -t --resources 看分段符号表即可证实。
二、分析方法:三个 xobjdump 用法
| 命令 | 作用 | 本文档中的用途 |
|---|---|---|
xobjdump -t app.xe | 打印 ELF 符号表 | 读每个函数的 .maxcores / .nstackwords / .maxtimers / .maxchanends,即编译器静态推导的「这个函数启动时要几个核、多少栈」 |
xobjdump --resources app.xe | 打印资源汇总报告 | 每 tile 的栈、程序大小、剩余内存、线程数、定时器、通道端、端口 |
xobjdump --size app.xe | 打印各 loadable 段大小 | text/data/bss 精确字节数,用于内存推算 |
原理:XC 编译器对每个包含 par / PJOB 的函数都会生成 4 个绝对符号(如 main_tile0.maxcores),数值等于该函数及其子作业对资源的静态需求总和。因此「哪个任务占几个核」不必靠猜,直接读符号即可;--resources 的汇总报告也是由这些信息聚合而来。
# 资源汇总(每 tile 的栈/程序/内存/线程/定时器/通道端/端口) xobjdump --resources application_xvf3800_ua-io48-lin.xe # 符号表(保存到文件后检索 *.maxcores / *.nstackwords) xobjdump -t application_xvf3800_ua-io48-lin.xe > syms.txt # 各 loadable 的 text/data/bss 字节数 xobjdump --size application_xvf3800_ua-io48-lin.xe
三、核资源模型:16 个逻辑核怎么分
XVF3800 采用 XMOS XU316 芯片:1 个节点(node)、2 个 tile,每个 tile 8 个逻辑核(logical core / 硬件线程),合计 16 个逻辑核;每个 tile 独立 512 KB SRAM(芯片合计 1 MB)。固件把这 16 个核分成两个「世界」:
裸金属世界(Bare metal):PJOB 直接在 RTOS 之外启动的作业,独占核、不被调度打断,负责音频实时通路(麦克风、AEC、后处理、I2S、USB 底层)。
FreeRTOS 世界:由 vTaskStartScheduler() 启动的 SMP 调度器,占用固定数量的核,所有 Servicer / 控制任务在这些核上轮转。
优先级方面,XMOS 没有抢占式优先级。所谓「高优先级(HP)」是通过 SETSR(XS1_SR_QUEUE_MASK) 把线程放入队列调度组(H 组)实现的——同一组的核每 5 个处理器周期轮流获得指令发射槽,多个核协同跑同一段 DSP,帧内耗时确定可预测。其余核为普通优先级(NP),平时 waiteu 等待事件唤醒。

[图 1 两个 tile 的核面分工:tile[0] 控制・USB・后处理 8/8,tile[1] 音频算法・I2S 8/8]
需要注意:逻辑核编号由运行时按 PJOB 启动顺序分配,图中「核槽位 0—7」只表示数量与归属,不代表固定物理编号;调试时可通过 get_logical_core_id() 打印实际编号。
四、源码层的核分配
两个 tile 的入口分别是 main_tile0() 与 main_tile1(),二者都在函数尾部用一个 PAR_JOBS(...) 一次性并行启动所有裸金属作业(每个 PJOB(func, args) 占用一个或多个逻辑核),其中最后一个 PJOB 固定为 tile_common_start_rtos_app(),负责初始化平台驱动并启动 FreeRTOS 调度器。
4.1 tile[0]:控制 / USB / 后处理面
PAR_JOBS( PJOB(INTERRUPT_PERMITTED(pp_task), (...)), // 3 核 PJOB(mic_array_task, (c_mic_to_audio)), // 1 核 PJOB(usb_buffer, (&usb_task_args)), // 1 核(UA) PJOB(wrap_XUD_Main, (...)), // 1 核(UA) PJOB(tile_common_start_rtos_app, (...)) // 2 核 );
| 作业(PJOB) | maxcores | nstackwords | 优先级 | 职责 |
|---|---|---|---|---|
pp_task | 3 | 0x506 = 5144 B | H 组 | SHF 后处理:波束成形、噪声抑制、DOA 输出;该核同时挂接 USB EP0 SETUP 包中断回调 |
mic_array_task | 1 | 0x5A = 360 B | 普通 | 4 路 PDM 麦克风采集中与抽取 |
usb_buffer | 1 | 0x3DC = 3952 B | 普通 | USB 音频端点软件缓冲、PLL 锁定检测 |
wrap_XUD_Main → XUD_Main | 1 | 0xB8 = 736 B | 普通 | lib_xud USB 设备栈底层收发 |
tile_common_start_rtos_app | 2 | 0x220 = 2176 B | 普通 | 启动 FreeRTOS SMP(UA 构建 configNUM_CORES = 2),GPO/PP/USB Buffer Server、ep_proxy 等任务都在这 2 个核上 |
合计 = 3 + 1 + 1 + 1 + 2 = 8 核。
4.2 tile[1]:音频算法 / I2S 数据面
PAR_JOBS( PJOB(aec_task, (...)), // 4 核 PJOB(audio_manager_task, (...)), // 1 核 PJOB(i2s_task, (...)), // 1 核 PJOB(reserved_core, ()), // 1 核 PJOB(tile_common_start_rtos_app, (...)) // 1 核 );
| 作业(PJOB) | maxcores | nstackwords | 优先级 | 职责 |
|---|---|---|---|---|
aec_task | 4 | 0x424 = 4240 B | H 组 | SHF 回声消除(SHF_Init_AEC_split_post,4 核协同) |
audio_manager_task | 1 | 0x20B = 2092 B | 普通 | 帧调度、声道混音;post_shf_dsp() 用户钩子在此执行 |
i2s_task | 1 | 0xFA = 1000 B | 普通 | I2S 主/从收发;far_end_dsp() 用户钩子在其回调中执行 |
reserved_core | 1 | 0x6 = 24 B | 普通 | 预留核:原为空转占位任务,是官方留给客户的核位 |
tile_common_start_rtos_app | 1 | 0x118 = 1120 B | 普通 | 启动 FreeRTOS(configNUM_CORES = 1),全部 Servicer、usb_manager、HID 都在这 1 个核上轮转 |
合计 = 4 + 1 + 1 + 1 + 1 = 8 核。

[图 2 两个 tile 的 PJOB 树与 maxcores 推导:子作业核数相加恰好等于 8]
4.3 官方注释与实际代码的差异
app_conf.h 中的官方注释把 tile[1] 描述为「4 HP AEC + 1 NP i2s/audio + 1 NP 客户 DSP + 1 备用 + 1 NP RTOS」。但实际代码里:
i2s_task与audio_manager_task是两个独立 PJOB(各占 1 核);「客户 DSP」位即
reserved_core;因此所谓「1 备用」在当前 8 核里并不存在——
main_tile1.maxcores = 8是静态可证的事实。
这份注释反映的是早期版本的设计意图,评估资源时请以符号表实测为准。
中篇预告:xobjdump 实测——--resources 汇总逐项解读、-t 符号表证据、tile[1] 那 5 行数字为什么是引导桩而非主应用、每 tile 512 KB 内存的推算方法。
下篇预告:UA 与 INT 构建的核心差异、数据流与核归属、工程建议、复现命令清单与常见误读澄清。