[TI] 存储空间_段_cfg的说明
1. 写在前面
本文拟对 CCS 使用过程中的一些设置,以及代码编写时需要注意的段分配做一些记录和说明。同时会大致介绍一下 cfg 的构成和编辑。
2. DSP 的存储空间分配
在编写 DSP 工程的过程中,需要将系统内有限的存储资源按需进行分配。在多核工程下更是如此,要将资源分配给不同的内核。
DSP内的存储资源采用分层的结构,以下表为例:
-
L1P和L1D的容量很小, 虽然它们可以配置为SRAM或者Cache,但是一般都作为Cache使用,分别缓存指令和数据。
-
片内的多核共享存储器控制器(Multicore Shared Memory Controller, MSMC)负责管理片上4 MB多核共享的静态随机访问存储器(Static Random Access Memory, SRAM),内核对这部分存储器的访问速度与L2缓存相当,仅次于L1缓存,属于容量较小的高速存储器。
-
片外的DDR3存储器也是由多核共享,但它是动态随机访问存储器(Dynamic Random Access Memory, DRAM),容量较大但访问速度较慢。
-
L2作为Cache部分的大小取决于DDR3 DRAM的使用情况,如果几乎没有使用DRAM, 那么L2都可以配置成SRAM;反之,如果DRAM使用较频繁,L2也需要相应地增大Cache的占比。
表 1 FT-M6678 片内片外存储资源
| 名称 | 大小 | 类别 | 说明 |
|---|---|---|---|
| L1D | 32 kB | 私有 | 一般作为 Cache 使用 |
| L1P | 32 kB | 私有 | 一般作为 Cache 使用 |
| L2 | 512 kB | 私有 | 可以部分配置为 Cache 或 SRAM,访问经过 L1 缓存 |
| MSMC SRAM | 4 MB | 共享 | 由 MSMC 管理的 SRAM,访问经过 L1 缓存 |
| DDR3 DRAM | 2 GB | 共享 | 容量最大但访问速度最慢,需要 L2 缓存加速 |
在实际开发过程中,软件工程需要对处理器的硬件平台(platform)进行配置,其配置界面如下图所示,在 CCS6.2 中通过 Project-RTSC Tools-Platform 打开。

平台的配置主要包括处理器型号,系统主时钟频率与不同存储空间的使用方式。
比如图中配置将L1都用作Cache,L2中的256 kB用作Cache,剩余256 kB用作SRAM。在MSMC SRAM与DDR3 DRAM中分别划分了256 kB和128 MB用作私有空间,同时还有512 kB的共享SRAM和1 GB的共享DRAM。而后将L2 SRAM用作代码区和栈区,私有的MSMC SRAM用作数据区。
2.1. 注意事项
在 FT-M6678 存储空间地址映射表中,每个 C66x CorePac 核均配备独立的 L1P、L1D 及 L2 SRAM。除各核对应的全局映射地址外,地址空间中还定义了一组 Local L1/L2 SRAM 区域,其含义与使用规则如下:
2.1.1. 局部地址与全局地址的双重映射
每个 CorePac 核的 L2 SRAM 均存在两种访问视图:
| 视图类型 | 地址范围(以 Core n 为例) | 访问权限 | 说明 |
|---|---|---|---|
| 局部地址(Local View) | 0x00800000 – 0x0087FFFF |
仅当前核自身可见 | 无论当前是 Core 0 还是 Core 7,每个核使用此局部地址访问到的都是本核私有的 L2 SRAM,其他主设备(Master)无法通过该地址访问此存储器。 |
| 全局地址(Global View) | 0x1n800000 – 0x1n87FFFF(其中 n 为 Core ID,n = 0~7) |
所有核及所有主设备可见 | 例如 Core 0 的全局 L2 地址为 0x10800000,Core 3 为 0x13800000。任何主设备(包括其他 Core、EDMA、SRIO 等)均可通过全局地址访问指定核的 L2 SRAM。 |
示例:Core 0 访问自身 L2 SRAM 时,既可以使用局部地址
0x00800000,也可以使用全局地址0x10800000;但 Core 1 若要访问 Core 0 的 L2 SRAM,只能使用全局地址0x10800000,无法使用局部地址0x00800000(因为该局部地址在 Core 1 的视角下指向的是 Core 1 自身的 L2 SRAM)。
2.1.2. L1 SRAM 的局部/全局映射关系
L1P 与 L1D 同样遵循上述双重映射机制:
| 存储器 | 局部基地址 | 全局基地址(Core n) |
|---|---|---|
| L1P SRAM | 0x00E00000 |
0x1nE00000 |
| L1D SRAM | 0x00F00000 |
0x1nF00000 |
其中,局部地址空间仅对所属 CorePac 本地有效,全局地址空间则允许跨核及外设主设备访问。
2.1.3. 工程实践中的注意事项
-
链接器/Platform 配置优先使用局部地址
在编写.cfg或链接器命令文件(.cmd)时,若将栈、私有数据或代码段分配到本核 L2 SRAM,建议使用局部地址(如0x00800000)。这样可以避免不同核的链接脚本因 Core ID 不同而需要分别维护,实现配置文件的统一。 -
核间通信必须使用全局地址
当某一核需要直接访问另一核的 L2 SRAM(如进行小数据量核间通信、共享状态标志等)时,必须在代码中显式使用目标核的全局地址(如0x13800000表示 Core 3 的 L2)。若误用局部地址,实际访问的将是本核自身的 L2 SRAM,导致数据错误或逻辑异常。 -
EDMA/SRIO 等主设备访问必须使用全局地址
外部主设备(如 EDMA、SRIO、HyperLink 等)不具备“局部视图”概念,其发起的所有访问均基于全局地址空间。因此,若需通过 EDMA 将数据搬入/搬出某核的 L2 SRAM,必须使用该核对应的全局地址作为源地址或目的地址。 -
Cache 一致性与地址别名问题
同一物理存储器存在局部与全局两个逻辑地址别名。若程序中混用两种地址访问同一物理位置,且该区域被配置为 Cacheable,可能引发 Cache 一致性问题。建议对同一数据块保持地址视图的一致性,或在必要时通过CACHE_wbInv()等操作手动维护 Cache 一致性。
3. 段含义
在 C6000 编译器(如用于 TMS320C6678)中,“段”(Section)是编译器和链接器管理代码与数据的基本单元。编译器将程序中的代码和初始化/未初始化的数据分别归类到不同的段中,每个段代表一块连续的内存区域。
段的核心作用在于实现内存的精细化布局:它允许开发者通过链接命令文件(.cmd)精确控制哪些代码或数据存放在片内高速 SRAM、哪些存放在片外 DDR 等特定物理地址上,从而优化系统性能并满足硬件约束。
此外,段还区分了只读属性(如常量、代码)和可写属性(如全局变量),并为运行时环境(如栈、堆、启动加载表)预留必要的空间,确保程序能够正确引导和执行。
以下是 C6000 编译器生成的主要段及其含义:
3.1. 已初始化段 (Initialized Sections)
这些段包含具体的代码或初始值数据,通常存储在非易失性存储器或受保护的 RAM 中。
| 段名称 | 类型 | 含义与用途 |
|---|---|---|
.text |
代码/只读 | 存放所有可执行代码及编译器生成的常数表。通常是只读的。 |
.const |
数据/只读 | 存放字符串字面量、浮点常量以及用 const 修饰的全局/静态变量。字符串会被放入子段 .const:.string 以便更灵活地定位。 |
.cinit |
数据/只读 | 仅在使用 --rom_model 选项时生成。包含显式初始化的全局和静态变量的拷贝表,用于启动时将初值从 ROM 复制到 RAM。 |
.binit |
数据/只读 | 包含启动时的拷贝表(Boot time copy tables),用于系统引导阶段的数据搬运。 |
.data |
数据/可写 | 显式初始化的全局和静态非 const 变量。存放程序员在定义时直接赋初值的普通全局/静态变量。 |
.fardata |
数据/可写 | 为非 const 的、使用 far 关键字声明的已初始化全局/静态变量预留空间。 |
.neardata |
数据/可写 | 为非 const 的、未使用 far 关键字(即 near)的已初始化全局/静态变量预留空间。 |
.rodata |
数据/只读 | 为使用 const 修饰的 near 全局和静态变量预留空间。 |
.switch |
数据/只读 | 存放大型 switch 语句生成的跳转表。 |
.args |
数据/可写 | 存放基于主机的加载器所需的命令行参数(需配合 --arg_size 选项)。 |
.init_array |
数据/只读 | 存放全局构造函数调用表,用于 C++ 全局对象的初始化。 |
.ovly |
数据/只读 | 存放覆盖(Overlay)拷贝表,用于多个段共享同一运行地址的场景。 |
.ppdata |
数据/可写 | 存放基于编译器的性能分析(Profiling)数据表。 |
.ppinfo |
数据/只读 | 存放性能分析的相关性信息表。 |
.TI.crctab |
数据/只读 | 存放 CRC 校验表。 |
.name.load |
数据/只读 | 存放名为 name 的段的压缩镜像,配合拷贝表使用。 |
.c6xabi.exidx |
数据/只读 | 异常处理索引表。 |
.c6xabi.extab |
数据/只读 | 异常处理的栈展开指令表。 |
3.2. 未初始化段 (Uninitialized Sections)
仅在内存中预留空间,不包含初始值,通常在运行时由程序动态填充,一般分配在 RAM 中。
| 段名称 | 类型 | 含义与用途 |
|---|---|---|
.bss |
数据/可写 | 为未初始化的全局和静态变量预留空间。未使用的变量可能被作为 common symbol 处理以节省空间。 |
.far |
数据/可写 | 为声明为 far 的全局和静态变量预留空间(无论是否初始化,此处特指未初始化部分的空间预留机制)。 |
.cio |
数据/可写 | 运行时支持库 stdio 函数的缓冲区。用于存放标准 I/O 操作(如 printf, scanf 等)所需的临时缓冲数据。 |
.stack |
系统/可写 | 为 C/C++ 系统栈预留空间。栈用于保存返回地址、局部变量、函数参数等,默认大小为 1KB,可通过链接器选项调整。 |
.sysmem |
系统/可写 | 为动态内存分配预留空间(堆)。供 malloc()、calloc()、new 等函数使用。若程序未调用这些函数,则不会生成此段。 |
3.3. 注意事项
- 链接器决定映射: 编译器只负责生成段,具体的物理内存地址分配完全由链接器根据 CMD 文件决定。
- 内部程序内存限制: 除代码段(.text 等)外,编译器生成的其他已初始化和未初始化段均不能分配到内部程序存储器(Internal Program Memory)中。 在链接时,已初始化段通常链接到 ROM 或 RAM,而未初始化段必须链接到 RAM。
4. cfg的构成
cfg(RTSC Configuration)是 TI XDCtools / RTSC 框架的配置脚本,使用 JavaScript 语法。它在编译阶段执行,用于:
- 导入并配置 SYS/BIOS、IPC、NDK 等模块;
- 定义内存段映射(sectMap),决定代码/数据放在 L2SRAM、MSMC 还是 DDR3;
- 创建静态对象:Task、Semaphore、Heap、SharedRegion 等;
- 配置系统级参数:栈大小、Cache 策略、多核同步、中断等。
编译时,XDC 工具会根据此 cfg 生成对应的 C 源码和链接器脚本,最终与用户的应用程序链接成可执行文件。
4.1. cfg示例代码
下面介绍一个典型的 cfg 代码组成,相关说明在注释里体现:
/*
* client.cfg
*
* Memory Map and Program initializations for the client example Utility
*/
/* ============================================================================
* 第一部分:导入 RTSC 模块(xdc.useModule / xdc.loadPackage)
*
* 所有以 xdc.useModule() 导入的模块,都会在生成的代码中创建对应的
* C 结构体/对象。xdc.loadPackage() 仅加载包,不自动创建实例。
* ============================================================================
*/
/* --- xdc.runtime 基础运行时模块 --- */
var Memory = xdc.useModule('xdc.runtime.Memory'); // 内存分配接口
var SysStd = xdc.useModule('xdc.runtime.SysStd'); // System_printf 的标准输出代理
var Error = xdc.useModule('xdc.runtime.Error'); // 错误处理框架
var Timestamp = xdc.useModule('xdc.runtime.Timestamp'); // 时间戳服务
var Log = xdc.useModule('xdc.runtime.Log'); // 日志记录
var Startup = xdc.useModule('xdc.runtime.Startup'); // 启动钩子(BIOS 启动前/后执行用户函数)
var System = xdc.useModule('xdc.runtime.System'); // System_printf 等系统服务
var Diags = xdc.useModule('xdc.runtime.Diags'); // 诊断级别控制(断言、日志等)
/* --- ti.sysbios 内核模块(RTOS 核心) --- */
var HeapBuf = xdc.useModule('ti.sysbios.heaps.HeapBuf'); // 固定大小缓冲池堆
var Clock = xdc.useModule('ti.sysbios.knl.Clock'); // 时钟/周期服务
var Idle = xdc.useModule('ti.sysbios.knl.Idle'); // 空闲任务钩子
var Event = xdc.useModule('ti.sysbios.knl.Event'); // 任务间事件标志
var Task = xdc.useModule('ti.sysbios.knl.Task'); // 任务(线程)管理
var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore'); // 信号量(二值/计数)
/* --- ti.sysbios HAL / C66x 特定模块 --- */
var Hwi = xdc.useModule('ti.sysbios.hal.Hwi'); // 硬件抽象层中断
var ti_sysbios_family_c64p_Hwi = xdc.useModule('ti.sysbios.family.c64p.Hwi'); // C64x+ 底层 Hwi
var CpIntc = xdc.useModule('ti.sysbios.family.c66.tci66xx.CpIntc'); // C66x 芯片级中断控制器
var Ecm = xdc.useModule('ti.sysbios.family.c64p.EventCombiner'); // 事件组合器(将多个系统事件映射到少量 CPU 中断)
var Cache = xdc.useModule('ti.sysbios.family.c66.Cache'); // C66x Cache 控制(L1P/L1D/L2)
var Exc = xdc.useModule('ti.sysbios.family.c64p.Exception'); // 异常处理模块
/* --- ti.sdo.ipc 核间通信模块 --- */
var Ipc = xdc.useModule('ti.sdo.ipc.Ipc'); // IPC 主控模块(核间同步)
var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion'); // 共享内存区域管理
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc'); // 多处理器 ID/名称管理
var Notify = xdc.useModule('ti.sdo.ipc.Notify'); // 核间通知(低开销中断)
var MessageQ = xdc.useModule('ti.sdo.ipc.MessageQ'); // 核间消息队列
var ListMP = xdc.useModule('ti.sdo.ipc.ListMP'); // 共享链表
var GateMP = xdc.useModule('ti.sdo.ipc.GateMP'); // 共享资源保护门
var HeapMemMP = xdc.useModule('ti.sdo.ipc.heaps.HeapMemMP'); // 跨核共享堆
/* --- ti.sysbios 同步原语 --- */
var GateMutex = xdc.useModule('ti.sysbios.gates.GateMutex'); // 互斥门(关中断实现)
/* --- 驱动与平台包 --- */
var Csl = xdc.useModule('ti.csl.Settings'); // 芯片支持库(CSL)设置
var Cppi = xdc.loadPackage('ti.drv.cppi'); // CPPI(数据包描述符管理)驱动包
var Qmss = xdc.loadPackage('ti.drv.qmss'); // QMSS(队列管理)驱动包
var Pa = xdc.useModule('ti.drv.pa.Settings'); // PA(包加速器)设置
var PlatformLib = xdc.loadPackage('ti.platform.evmc6678l'); // EVM6678L 平台包(含内存映射)
var NdkTransport = xdc.loadPackage('ti.transport.ndk'); // NDK 网络传输层
/* --- NDK 网络栈配置 --- */
var Global = xdc.useModule('ti.ndk.config.Global'); // NDK 全局配置
Global.enableCodeGeneration = false; // 不自动生成 NDK 服务线程,由用户代码手动创建
/* --- BIOS 主控模块 --- */
var BIOS = xdc.useModule('ti.sysbios.BIOS'); // SYS/BIOS 内核主模块
/* ============================================================================
* 第二部分:系统级参数配置
* ============================================================================
*/
// 允许任务保存友好名称(用于调试和 ROV 查看)
Task.common$.namedInstance = true;
// 配置异常日志:使用 LoggerBuf,异常信息可透过 CCS ROV 查看
var LoggerBuf = xdc.useModule('xdc.runtime.LoggerBuf');
Exc.common$.logger = LoggerBuf.create(); // 绑定日志实例
Exc.enablePrint = true; // 异常时打印到 CCS 控制台
// System_printf 输出代理设为 SysStd(标准输出,通常映射到 UART/CCS 控制台)
System.SupportProxy = SysStd;
/* ============================================================================
* 第三部分:堆(Heap)与栈(Stack)配置
*
* 堆:动态内存分配的来源(malloc、Task 创建、IPC 对象等)
* 栈:函数调用、局部变量、中断现场的保存区域
* ============================================================================
*/
// 创建一个本地堆(HeapMem),用于本核内的动态内存分配
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var heapMemParams = new HeapMem.Params();
heapMemParams.size = 0x54000; // 堆大小:344KB(0x54000 = 344 * 1024)
heapMemParams.sectionName = "systemHeap"; // 该堆所在的链接段名
Program.global.heap0 = HeapMem.create(heapMemParams);
// 系统栈大小:0x3000 = 12KB,用于 ISR、Hook、Idle 等系统级上下文
Program.stack = 0x3000;
// 设置默认堆实例:后续未显式指定堆的 malloc/Task 创建,都使用 heap0
Memory.defaultHeapInstance = Program.global.heap0;
/* ============================================================================
* 第四部分:段映射(Section Mapping)—— 决定代码/数据放在哪里
*
* Program.sectMap["段名"] = "存储器名";
* 将编译器生成的各段映射到 Platform 中定义的存储器(L2SRAM / MSMC / DDR3)
*
* 策略说明:
* - .text(代码段)→ DDR3:代码量大,DDR3 容量充足(2GB)
* - .stack / taskStackSection → L2SRAM:栈必须低延迟、确定性访问
* - 其余数据段(.bss/.data/.const/.far 等)→ MSMC:4MB 共享高速内存
* - NDK 相关缓冲区、描述符 → L2SRAM:网络包处理需要低延迟
* ============================================================================
*/
// 大部分数据段放在 MSMC(4MB 共享 SRAM,延迟低于 DDR3)
Program.sectMap["sharedL2"] = "MSMC";
Program.sectMap["systemHeap"] = "MSMC";
Program.sectMap[".sysmem"] = "MSMC";
Program.sectMap[".args"] = "MSMC";
Program.sectMap[".cio"] = "MSMC";
Program.sectMap[".far"] = "MSMC";
Program.sectMap[".rodata"] = "MSMC";
Program.sectMap[".neardata"] = "MSMC";
Program.sectMap[".cppi"] = "MSMC";
Program.sectMap[".init_array"] = "MSMC";
Program.sectMap[".qmss"] = "MSMC";
Program.sectMap[".cinit"] = "MSMC";
Program.sectMap[".bss"] = "MSMC";
Program.sectMap[".const"] = "MSMC";
Program.sectMap[".code"] = "MSMC";
Program.sectMap[".switch"] = "MSMC";
Program.sectMap[".data"] = "MSMC";
Program.sectMap[".fardata"] = "MSMC";
Program.sectMap[".vecs"] = "MSMC";
Program.sectMap["platform_lib"]= "MSMC";
Program.sectMap[".DbgSection"] = "MSMC";
// 代码段放在 DDR3:容量大,适合存放完整程序代码
Program.sectMap[".text"] = "DDR3";
// 栈和任务栈放在 L2SRAM:每核私有,访问延迟最低
Program.sectMap[".far:taskStackSection"] = "L2SRAM";
Program.sectMap[".stack"] = "L2SRAM";
// NDK / 网络相关缓冲区放在 L2SRAM,满足网络收发的低延迟需求
Program.sectMap[".nimu_eth_ll2"] = "L2SRAM";
Program.sectMap[".resmgr_memregion"] = {loadSegment: "L2SRAM", loadAlign:128};
Program.sectMap[".resmgr_handles"] = {loadSegment: "L2SRAM", loadAlign:16};
Program.sectMap[".resmgr_pa"] = {loadSegment: "L2SRAM", loadAlign:8};
Program.sectMap[".far:IMAGEDATA"] = {loadSegment: "L2SRAM", loadAlign: 8};
Program.sectMap[".far:NDK_OBJMEM"] = {loadSegment: "L2SRAM", loadAlign: 8};
Program.sectMap[".far:NDK_PACKETMEM"]= {loadSegment: "L2SRAM", loadAlign: 128};
Program.sectMap[".NDKDESC"] = {loadSegment: "L2SRAM", loadAlign: 128};
// 图像数据缓冲区放在 DDR3:数据量大,DDR3 容量充足
Program.sectMap[".image"] = new Program.SectionSpec();
Program.sectMap[".image"].loadSegment = "DDR3";
/* ============================================================================
* 第五部分:IPC 多核配置
*
* 配置核间同步、多处理器 ID、以及共享内存区域(SharedRegion)。
* SharedRegion 是 IPC 模块(MessageQ、Notify 等)进行核间通信的基础。
* ============================================================================
*/
// 所有核对齐同步:所有核启动时通过 IPC 握手,确保同时进入 main()
Ipc.procSync = Ipc.ProcSync_ALL;
// 多处理器配置:当前为单核运行(CORE0),baseIdOfCluster 为簇内起始 ID
MultiProc.baseIdOfCluster = 0;
MultiProc.numProcessors = 1;
MultiProc.setConfig("CORE0", ["CORE0"]);
// 定义 2 个共享区域:MSMC(高速共享)和 DDR3(大容量共享)
SharedRegion.numEntries = 2;
SharedRegion.translate = false; // 关闭地址翻译,直接使用物理/逻辑地址
// SharedRegion 0:MSMC 共享内存(0x0C000000,512KB)
// 用于 MessageQ、HeapMemMP 等 IPC 对象的快速核间通信
var SMSMC_BASE = 0x0C000000;
var SMSMC_SIZE = 0x00080000; // 512KB
SharedRegion.setEntryMeta(0, {
base: SMSMC_BASE,
len: SMSMC_SIZE,
ownerProcId: 0, // CORE0 拥有管理权
isValid: true,
cacheEnable: true, // 使能 Cache,需软件维护一致性
cacheLineSize: 64, // Cache 行大小 64 字节
createHeap: true, // 自动在该区域创建共享堆
name: "MSMC_SHARED",
});
// SharedRegion 1:DDR3 共享内存(0x80000000,1GB)
// 用于大数据量共享缓冲,或作为 MSMC 不足时的后备
var SDDR3_BASE = 0x80000000;
var SDDR3_SIZE = 0x40000000; // 1GB
SharedRegion.setEntryMeta(1, {
base: SDDR3_BASE,
len: SDDR3_SIZE,
ownerProcId: 0,
isValid: true,
cacheEnable: true,
cacheLineSize: 128, // DDR3 Cache 行对齐 128 字节
createHeap: true,
name: "DDR3_SHARED",
});
/* ============================================================================
* 第六部分:静态任务(Task)创建
*
* 在 cfg 中创建的任务是静态任务,编译时即分配好 TCB 和栈空间,
* 比运行时 Task_create() 效率更高,且不占动态堆。
* ============================================================================
*/
// NDK 网络栈任务:负责 TCP/IP 协议栈运行
var NdkStackTaskparam = new Task.Params;
NdkStackTaskparam.instance.name = "hNdkStackTask";
NdkStackTaskparam.priority = 10; // 优先级 10(数值越大优先级越高)
NdkStackTaskparam.stackSize = 0x15400; // 栈大小:约 85KB
Program.global.hNdkStackTask = Task.create("&NdkStackTskFxn", NdkStackTaskparam);
// 主任务:用户应用程序入口
var MainTaskparam = new Task.Params;
MainTaskparam.instance.name = "hMainTask";
MainTaskparam.priority = 12; // 优先级高于 NDK 任务
MainTaskparam.stackSize = 2048; // 栈大小:2KB
Program.global.hMainTask = Task.create("&MainTskFxn", MainTaskparam);
/* ============================================================================
* 第七部分:信号量(Semaphore)创建
*
* 二值信号量用于任务间同步:例如网络初始化完成通知、数据处理触发等。
* ============================================================================
*/
// TCP 初始化完成信号量
var semphore0Params = new Semaphore.Params();
semphore0Params.instance.name = "hTCPInitSem";
semphore0Params.mode = Semaphore.Mode_BINARY;
Program.global.hTCPInitSem = Semaphore.create(null, semphore0Params);
// NAND 操作句柄信号量
var semphore1Params = new Semaphore.Params();
semphore1Params.instance.name = "nandHandleSem";
semphore1Params.mode = Semaphore.Mode_BINARY;
Program.global.nandHandleSem = Semaphore.create(null, semphore1Params);
// 状态发送信号量
var semphore2Params = new Semaphore.Params();
semphore2Params.instance.name = "statusSendSem";
semphore2Params.mode = Semaphore.Mode_BINARY;
Program.global.statusSendSem = Semaphore.create(null, semphore2Params);
// NAND 下载触发信号量
var semphore3Params = new Semaphore.Params();
semphore3Params.instance.name = "nandDownloadSem";
semphore3Params.mode = Semaphore.Mode_BINARY;
Program.global.nandDownloadSem = Semaphore.create(null, semphore3Params);
// 数据处理触发信号量
var semphore4Params = new Semaphore.Params();
semphore4Params.instance.name = "dataHandleSem";
semphore4Params.mode = Semaphore.Mode_BINARY;
Program.global.dataHandleSem = Semaphore.create(null, semphore4Params);
/* ============================================================================
* 第八部分:其他系统配置
* ============================================================================
*/
// 使能 BIOS 任务调度器
BIOS.taskEnabled = true;
// 关闭 GateMutex 的断言诊断,减少运行时开销
GateMutex.common$.diags_ASSERT = Diags.ALWAYS_OFF;
/*
* 事件组合器(EventCombiner)配置:
* C66x 有 128+ 系统事件,但 CPU 中断输入只有 12-15 个(INT4-INT15)。
* EventCombiner 将系统事件分组映射到 4 个 CPU 中断(7/8/9/10),
* 再通过 EventCombiner_dispatchPlug() 在 C 代码中注册具体 ISR。
*/
Ecm.eventGroupHwiNum[0] = 7;
Ecm.eventGroupHwiNum[1] = 8;
Ecm.eventGroupHwiNum[2] = 9;
Ecm.eventGroupHwiNum[3] = 10;
5. 补充-Task 栈与系统栈的区别及分配机制
TMS320C6678: SYS/BIOS 中 Task 动态创建和静态时,如果未指定 .stackHeap 和 .stackSection,那么栈空间会来源于哪里?
在 SYS/BIOS 中,必须严格区分系统栈(System Stack)与任务栈(Task Stack)。二者在用途、生命周期与可控性上完全不同。
5.1. 系统栈
Program.stack 定义的是系统栈,仅在以下场景使用:
- 程序启动初始化阶段(main() 执行期间,即 MAIN 上下文)
- 中断服务程序(ISR)、Hook 函数、Idle 循环等系统级上下文
系统栈不会自动分割给各个 Task 使用。一旦 BIOS 调度器启动并切换到 Task 上下文,CPU 的 SP 指针将指向该 Task 独立的私有栈,系统栈在此阶段处于非活动状态。
5.2. 任务栈的独立性与三种供给方式
SYS/BIOS 为每个 Task 实例分配独立的栈空间,以避免单个任务的栈溢出或错误写操作破坏其他任务的运行环境。任务栈的供给方式分为以下三种:
| 方式 | 适用场景 | 核心机制 |
|---|---|---|
| 链接段预分配 | .cfg 中静态创建的任务 |
由链接器在编译期预留,通过 Task_Params.stackSection 或 Task.defaultStackSection 指定段名,再由 Program.sectMap 映射到具体存储器 |
| 堆实例动态分配 | 运行时 Task_create() |
通过 Task_Params.stackHeap 指定堆实例;若不指定,则从 BIOS 默认堆分配 |
| 用户预分配缓冲区 | 需精确控制栈物理地址 | 在 C 代码中定义全局数组并通过编译器 pragma 放置到指定段,然后在 Task_Params 中传入 .stack 指针与 .stackSize |
5.3. 静态任务栈的链接段控制
对于在 .cfg 中通过 Task.create() 静态创建的任务,C6x 目标(如 FT-M6678)的默认任务栈段名为 .far:taskStackSection。可通过以下方式控制其最终存储位置:
/* 方式一:直接映射默认任务栈段到 L2SRAM */
Program.sectMap[".far:taskStackSection"] = "L2SRAM";
/* 方式二:自定义段名并映射到 MSMC */
Task.defaultStackSection = ".myTaskStacks";
Program.sectMap[".myTaskStacks"] = {loadSegment: "MSMC", loadAlign: 8};
若需为单个静态任务指定独立段,可在该任务的参数中显式声明:
var taskParams = new Task.Params;
taskParams.stackSection = ".ndkStack"; // 自定义段名
Program.sectMap[".ndkStack"] = "L2SRAM";
Program.global.hNdkStackTask = Task.create("&NdkStackTskFxn", taskParams);
5.4. 动态任务栈的堆实例控制
对于运行时通过 Task_create() 动态创建的任务,无法直接指定链接段,但可通过 Task_Params.stackHeap 指定从哪个堆实例分配栈空间:
(1)在 .cfg 中创建指向特定存储器的专用堆
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var l2HeapParams = new HeapMem.Params();
l2HeapParams.size = 0x20000;
l2HeapParams.sectionName = ".l2Heap";
Program.global.l2Heap = HeapMem.create(l2HeapParams);
Program.sectMap[".l2Heap"] = "L2SRAM";
(2)在 C 代码中创建任务时引用该堆
Task_Params params;
Task_Params_init(¶ms);
params.stackHeap = l2Heap; /* 从 L2SRAM 的堆中分配任务栈 */
params.stackSize = 0x10000;
Task_Handle hTask = Task_create(myFxn, ¶ms, NULL);
(3)使用默认堆
若 Task_Params 中不指定 stackHeap,则任务栈从 BIOS 默认堆(由 BIOS.heapSize 或 Program.heap 定义)中分配。该默认堆的物理位置由 .cfg 中的段映射(如 Program.sectMap[".sysmem"])决定。
5.5. 使用预分配缓冲区作为任务栈
若需将任务栈放置在任意指定地址(如 MSMC 的某个固定偏移),可在 C 代码中预分配缓冲区并直接传入栈指针:
#pragma DATA_SECTION(ndkStackBuf, ".ndkStackSec")
#pragma DATA_ALIGN(ndkStackBuf, 8)
Uint8 ndkStackBuf[0x15400]; /* 预分配 85KB 栈缓冲区 */
Task_Params params;
Task_Params_init(¶ms);
params.stack = ndkStackBuf; /* 直接传入预分配缓冲区指针 */
params.stackSize = sizeof(ndkStackBuf);
Task_Handle hTask = Task_create(NdkStackTskFxn, ¶ms, NULL);
对应的 .cfg 中需将该段映射到目标存储器:
Program.sectMap[".ndkStackSec"] = "L2SRAM";
注意:使用
.stack指针方式时,用户必须确保缓冲区大小满足该任务的最大调用深度需求,且起始地址满足目标架构的对齐要求(C6x 通常要求 8 字节对齐)。SYS/BIOS 不会对该用户提供的缓冲区进行溢出保护检查。

浙公网安备 33010602011771号