CCS之CMD(链接命令)文件讲解
CCS之CMD(链接命令)文件讲解
CMD文件是TI编译工具链中链接器的配置文件,它通过MEMORY块声明芯片各块RAM和Flash的物理地址与大小,通过SECTIONS块将编译器生成的各种段(代码、变量、常量、栈堆等)分配到指定内存,本质上是给链接器画一张"内存地图",决定程序每个部分在芯片上电后实际存放在哪里、从哪里取指和执行。
本文以 F28388S 为例说明,不同系列芯片内存分布不同,此处仅做参考
一、C28x 与 CM 的 RAM 类型及归属
C2000 的 RAM 不是一块连续的通用内存随便用,而是按访问主体(哪个核/外设能用)、访问权限(只读/读写/取指)和延迟特性(紧耦合/共享/带 ECC)划分的。理解硬件归属是写 CMD 的前提
1.1 RAMM0 / RAMM1(专用RAM)
M0 和 M1 是 C28x 最靠近 CPU 流水线的两块紧耦合小容量 RAM,各 1K×16 = 2 KB。每个 C28x 子系统物理独立,双核工程中 CPU1 和 CPU2 各有自己的 M0/M1,互不干扰。特点是零等待,但容量极小,不适合放大块数据。
M0 的起始段(0x000002 起约 0x1AF 字)被 Boot ROM 占用,上电自举时当临时栈和变量区,这部分在 CMD 里声明为 BOOT_RSVD,用户代码绝对不能覆盖。剩余可用部分从 0x0001B1 开始,总共约 600 字,只适合放冷启动关键的小标志或查表数据。
M1 传统上放 .stack(C 函数调用栈),起始地址 0x000400,长度 0x3F8(而非 0x400),尾部 8 个字(0x07F8–0x07FF)按芯片勘误保留,防止预取越界。如果中断嵌套深或局部变量大,1K 字栈不够用,需要把栈改到 LSx 或 D0/D1 块。
1.2 RAMD0 / RAMD1(专用RAM)
D0 和 D1 各 2K×16 = 4 KB,地址分别为 0x00C000 和 0x00C800,归属本 C28x 子系统独占。与 M/LS/GS 不同,D 块带 ECC(单比特纠错、双比特报错),并且可配置写保护、取指保护和 DCSM 安全分区。
适合存放安全密钥、功能安全关键状态、不希望被 DMA 或 CLA 意外改写的变量。如果单核工程不跑安全功能,也可以当普通高速数据区,但要注意别和 CLA/DMA 共享同一块。
1.3 RAMLS0 ~ RAMLS7(本地共享RAM)
LS 块共 8 块,每块 2K×16 = 4 KB,地址范围 0x008000–0x00BFFF,合计 32KB。归属本 C28x 子系统加本 CLA 子系统,带 ECC,可通过 MemCfg 寄存器动态切换用途。默认仅供CPU使用,可通过配置选择与CLA共享这段存储器。
1.4 GSX RAM(全局共享 RAM)
GS可以有多块,如RAMGS0、RAMGS1等,每块 4K×16 = 8 KB。双 C28x 核以及各自的 DMA 都能看到这块地址空间,通过 GSxMSEL 寄存器指定主从关系。带 parity 校验(非 ECC),硬件强制主从权限。
主核(Master)可读可写可取指;从核(Slave)默认只读,写操作会触发 NMI 或被忽略。上电默认 CPU1 为所有 GS 的主人,CPU1 通过 MemCfg_setGSRAMMasterSel() 把某块判给 CPU2。
GS 适合双核大容量数据交换——通信帧缓冲、日志存储、DMA 中转等。小块核间通信不要用 GS,应当走专用消息 RAM。
1.5 消息 RAM(MSG RAM)—— 核间/外设专用邮箱
消息 RAM 不是通用 RAM,硬件固定了访问方向,只适合放命令字、状态标志、小结构体,不存放大数组。
CPU 间通信:CPU1TOCPU2RAM(0x03A000,CPU1 写→CPU2 读)和 CPU2TOCPU1RAM(0x03B000,CPU2 写→CPU1 读),各 0x800 字(4KB)。
CPU 与 CM 通信:CPUTOCMRAM(0x039000,C28x 写→CM 读)和 CMTOCPURAM(0x038000,CM 写→C28x 读),各 0x800 字。
CPU 与 CLA 通信:CLA1_MSGRAMLOW(0x001480,CPU 写→CLA 读,256 字)和 CLA1_MSGRAMHIGH(0x001500,CLA 写→CPU 读,256 字)。另有 CLA1_DMA_MSGRAM 和 DMA_CLA1_MSGRAM 各 128 字供 DMA 与 CLA 交互。
CAN 邮箱:CANA_MSG_RAM(0x049000)和 CANB_MSG_RAM(0x04B000),各 0x800 字,由 DCAN 硬件直接管理 message object。
所有消息 RAM 通常设为 type=NOINIT(上电不自动清零),配合 IPC 中断使用。
1.6 CM(Cortex-M4)独立 RAM
在高级的DSP系列中,会有ARM内核,F2838x 的 CM 子系统有自己独立的地址空间,不按 C28x 的 M/LS/GS 分类。CM 工程使用 Arm 链接脚本(.ld 格式),C28x 的 CMD 里只能声明 CPUTOCMRAM / CMTOCPURAM 消息块,不能分配 CM 的业务 RAM。
二、CMD 文件基本结构
CMD 文件只干两件事:声明物理内存(MEMORY)和分配段到内存(SECTIONS)。
2.1 SECTIONS 常见段
| 段名 | 描述 |
|---|---|
codestart |
复位引导分支,跳 _c_int00/main。固定 2 字,放 BEGIN。 |
.reset |
复位向量伪段。固定 2 字,设 TYPE=DSECT 不分配用户数据。 |
.text |
普通函数代码。大小取决于函数数量和库的使用。代码量大时用 >> 分散到多块 RAM。 |
.TI.ramfunc |
Flash 加载、RAM 执行的关键函数。Flash 启动时需 LOAD=Flash, RUN=RAM 分离。 |
.switch |
switch-case 跳转表。通常几十字,放小块 RAM 即可。 |
.cinit |
C 全局变量初始化表。大小取决于有初值的全局变量数量。必须连续存放(用 > 不用 >>)。 |
.const |
常量数据(const 数组、字符串等)。EABI 下使用,通常放 Flash 或 GS 块。 |
.econst |
COFF 常量数据。用法同 .const,COFF ABI 下使用。 |
.bss |
未初始化全局/静态变量。RAM 消耗大户,启动时自动清零。 |
.bss:output |
.bss 的子段,用于将特定变量单独分配到指定 RAM 块。 |
.data |
已初始化数据本体。运行时在 RAM,初值由 .cinit 表从 Flash 拷贝(RAM运行无需拷贝)。 |
.sysmem |
malloc 堆。大小由 --heap_size 配置,单独划一块 RAM。 |
.init_array |
C++ 构造函数表。C 工程可忽略。 |
.pinit |
COFF 下的 C++ 构造函数表。EABI 下用 .init_array 替代。 |
.ebss |
COFF 下的未初始化变量段。EABI 下用 .bss 替代。 |
.esysmem |
COFF 下的 malloc 堆。EABI 下用 .sysmem 替代。 |
.stack |
C 调用栈。取决于调用深度和中断嵌套。传统放 RAMM1,不够时改 LS/D 块。 |
MSGRAM_CPU1_TO_CPU2 |
CPU1→CPU2 核间消息区。单向邮箱,设 type=NOINIT。 |
MSGRAM_CPU2_TO_CPU1 |
CPU2→CPU1 核间消息区。单向邮箱,设 type=NOINIT。 |
MSGRAM_CPU_TO_CM |
C28x→CM 核间消息区。单向邮箱,设 type=NOINIT。 |
MSGRAM_CM_TO_CPU |
CM→C28x 核间消息区。单向邮箱,设 type=NOINIT。 |
ramgs0 |
用户自定义段,放 RAMGS0,设 type=NOINIT。用于精确控制变量位置。 |
ramgs1 |
用户自定义段,放 RAMGS1,设 type=NOINIT。用于精确控制变量位置。 |
dclfuncs |
DCL(数字控制库)函数段。放高速 RAM 以保证控制周期性能。 |
上表中 ramgs0、ramgs1 等为用户自定义段,配合 #pragma DATA_SECTION 可将变量精确放置到指定 RAM 块:
#pragma DATA_SECTION(adcAResult, "ramgs0")
volatile uint16_t adcAResult[6]; // 变量固定分配到 RAMGS0
CLA 相关段名多为用户/库自定义,核心仅两点:Cla1Prog 独占 LS0(CLA 程序模式),共享数据放 LS1/LS2。实际工程按需删减即可,下文示例仅作参考。
2.4 示例
以下为 RAM 启动配置(F28388S 的 CPU1+CLA),所有段直接下载到 RAM,上电即用。
MEMORY
{
/* BEGIN is used for the "boot to SARAM" bootloader mode */
BEGIN : origin = 0x000000, length = 0x000002
BOOT_RSVD : origin = 0x000002, length = 0x0001AF /* Part of M0, BOOT rom will use this for stack */
RAMM0 : origin = 0x0001B1, length = 0x00024F
RAMM1 : origin = 0x000400, length = 0x0003F8 /* on-chip RAM block M1 */
// RAMM1_RSVD : origin = 0x0007F8, length = 0x000008 /* Reserve and do not use for code as per the errata advisory "Memory: Prefetching Beyond Valid Memory" */
RAMD0 : origin = 0x00C000, length = 0x000800
RAMD1 : origin = 0x00C800, length = 0x000800
RAMLS0 : origin = 0x008000, length = 0x000800
RAMLS1 : origin = 0x008800, length = 0x000800
RAMLS2 : origin = 0x009000, length = 0x000800
RAMLS3 : origin = 0x009800, length = 0x000800
RAMLS4 : origin = 0x00A000, length = 0x000800
RAMLS5 : origin = 0x00A800, length = 0x000800
RAMLS6 : origin = 0x00B000, length = 0x000800
RAMLS7 : origin = 0x00B800, length = 0x000800
RAMGS0 : origin = 0x00D000, length = 0x001000
RAMGS1 : origin = 0x00E000, length = 0x001000
RAMGS2 : origin = 0x00F000, length = 0x001000
RAMGS3 : origin = 0x010000, length = 0x001000
RAMGS4 : origin = 0x011000, length = 0x001000
RAMGS5 : origin = 0x012000, length = 0x001000
RAMGS6 : origin = 0x013000, length = 0x001000
RAMGS7 : origin = 0x014000, length = 0x001000
RAMGS8 : origin = 0x015000, length = 0x001000
RAMGS9 : origin = 0x016000, length = 0x001000
RAMGS10 : origin = 0x017000, length = 0x001000
RAMGS11 : origin = 0x018000, length = 0x001000
RAMGS12 : origin = 0x019000, length = 0x001000
RAMGS13 : origin = 0x01A000, length = 0x001000
RAMGS14 : origin = 0x01B000, length = 0x001000
RAMGS15 : origin = 0x01C000, length = 0x000FF8
// RAMGS15_RSVD : origin = 0x01CFF8, length = 0x000008 /* Reserve and do not use for code as per the errata advisory "Memory: Prefetching Beyond Valid Memory" */
/* Flash sectors */
FLASH0 : origin = 0x080000, length = 0x002000 /* on-chip Flash */
FLASH1 : origin = 0x082000, length = 0x002000 /* on-chip Flash */
FLASH2 : origin = 0x084000, length = 0x002000 /* on-chip Flash */
FLASH3 : origin = 0x086000, length = 0x002000 /* on-chip Flash */
FLASH4 : origin = 0x088000, length = 0x008000 /* on-chip Flash */
FLASH5 : origin = 0x090000, length = 0x008000 /* on-chip Flash */
FLASH6 : origin = 0x098000, length = 0x008000 /* on-chip Flash */
FLASH7 : origin = 0x0A0000, length = 0x008000 /* on-chip Flash */
FLASH8 : origin = 0x0A8000, length = 0x008000 /* on-chip Flash */
FLASH9 : origin = 0x0B0000, length = 0x008000 /* on-chip Flash */
FLASH10 : origin = 0x0B8000, length = 0x002000 /* on-chip Flash */
FLASH11 : origin = 0x0BA000, length = 0x002000 /* on-chip Flash */
FLASH12 : origin = 0x0BC000, length = 0x002000 /* on-chip Flash */
FLASH13 : origin = 0x0BE000, length = 0x002000 /* on-chip Flash */
CPU1TOCPU2RAM : origin = 0x03A000, length = 0x000800
CPU2TOCPU1RAM : origin = 0x03B000, length = 0x000800
CPUTOCMRAM : origin = 0x039000, length = 0x000800
CMTOCPURAM : origin = 0x038000, length = 0x000800
CANA_MSG_RAM : origin = 0x049000, length = 0x000800
CANB_MSG_RAM : origin = 0x04B000, length = 0x000800
RESET : origin = 0x3FFFC0, length = 0x000002
CLA1_MSGRAMLOW : origin = 0x001480, length = 0x000080
CLA1_MSGRAMHIGH : origin = 0x001500, length = 0x000080
CLA1_DMA_MSGRAM : origin = 0x001680, length = 0x000080
DMA_CLA1_MSGRAM : origin = 0x001700, length = 0x000080
}
SECTIONS
{
/* 复位与入口 */
codestart : > BEGIN
.reset : > RESET, TYPE = DSECT
/* ========== 代码段 ========== */
/* RAM启动:所有代码进RAM,铺到D0/D1/LS/GS大块 */
.text : >> RAMD0 | RAMD1 | RAMLS2 | RAMLS3 | RAMLS4 | RAMLS5 | RAMLS6 | RAMLS7 | RAMGS0
.TI.ramfunc : >> RAMD0 | RAMGS0 /* RAM启动下并入大块RAM */
.switch : > RAMM0 /* 跳转表,很小,留M0 */
/* ========== 初始化与常量 ========== */
.cinit : > RAMGS1 /* 初始化表,给足空间 */
.const : >> RAMGS2 | RAMGS3 /* EABI常量 */
.econst : >> RAMGS2 | RAMGS3 /* COFF常量 */
#if defined(__TI_EABI__)
.bss : >> RAMGS3 | RAMGS4 | RAMGS5 /* 未初始化变量,大块GS */
.bss:output : > RAMGS3
.data : >> RAMGS3 | RAMGS4 /* 已初始化数据 */
.sysmem : > RAMGS6 /* malloc堆 */
.init_array : > RAMM0 /* C++构造,很小 */
#else
.pinit : > RAMM0
.ebss : >> RAMGS3 | RAMGS4 | RAMGS5 /* COFF未初始化变量 */
.esysmem : > RAMGS6
#endif
/* ========== 栈 ========== */
.stack : > RAMM1 /* 1K字栈,够用 */
/* ========== 核间消息RAM(NOINIT) ========== */
MSGRAM_CPU1_TO_CPU2 : > CPU1TOCPU2RAM, type=NOINIT
MSGRAM_CPU2_TO_CPU1 : > CPU2TOCPU1RAM, type=NOINIT
MSGRAM_CPU_TO_CM : > CPUTOCMRAM, type=NOINIT
MSGRAM_CM_TO_CPU : > CMTOCPURAM, type=NOINIT
/* ========== CLA 配置 ========== */
/* CLA程序:独占LS0,CPU不能当数据访问 */
Cla1Prog : > RAMLS0
/* CLA共享数据:LS1+LS2 */
Cla1DataRam : >> RAMLS1 | RAMLS2
CLADataLS0 : > RAMLS1
CLADataLS1 : > RAMLS2
*****ared : > RAMLS1
.bss_cla : > RAMLS1
.const_cla : > RAMLS1
/* CLA消息RAM —— 修正方向 */
CpuToCla1MsgRAM : > CLA1_MSGRAMLOW, type=NOINIT /* CPU写→CLA读,LOW块 */
Cla1ToCpuMsgRAM : > CLA1_MSGRAMHIGH, type=NOINIT /* CLA写→CPU读,HIGH块 */
Cla1ToDmaMsgRAM : > CLA1_DMA_MSGRAM, type=NOINIT
DmaToCla1MsgRAM : > DMA_CLA1_MSGRAM, type=NOINIT
/* CLA编译器要求的scratch区 */
CLAscratch :
{
*.obj(CLAscratch)
. += CLA_SCRATCHPAD_SIZE;
*.obj(CLAscratch_end)
} > RAMLS2
.scratchpad : > RAMLS2
/* ========== 用户自定义GS区 ========== */
ramgs0 : > RAMGS0, type=NOINIT
ramgs1 : > RAMGS1, type=NOINIT
/* ========== 其他 ========== */
dclfuncs : >> RAMD0 | RAMGS0 /* DCL库函数,放高速RAM */
}

浙公网安备 33010602011771号