一、先搞懂 F28335 的核心硬件内存概念
在看 cmd 文件之前,必须先明确 F28335 的物理内存布局,这是 cmd 文件的底层依据。
1. F28335 的内存分类与 PAGE 机制( PAGE 0/PAGE 1)
F28335 是基于哈佛架构 的 DSP,哈佛架构的核心特点是程序存储器和数据存储器分开寻址 ,TI 为了适配这个架构,在 F28335 中引入了PAGE 0 和PAGE 1的逻辑分区机制(注意:不是物理上的两个内存芯片,是逻辑分区)。
| 分区 | 作用 | 对应物理内存类型 |
|---|---|---|
| PAGE 0 | 程序内存(Program) | 存储代码、常量、中断向量、BootROM |
| PAGE 1 | 数据内存(Data) | 存储变量、堆栈、外设寄存器、DMA 数据 |
关键注意点:
- 同一个物理内存地址不能同时被定义到 PAGE 0 和 PAGE 1 (比如不能把 RAML0 既放在 PAGE 0 又放在 PAGE 1),否则会导致程序 / 数据冲突、系统崩溃(这也是 cmd 文件中
MEMORY部分注释里强调的点)。 - F28335 的物理内存是统一编址 的(即每个物理内存有唯一的地址),PAGE 0/PAGE 1 只是链接器和 CPU 寻址时的逻辑划分。
2. F28335 的物理内存组成(对应 cmd 文件中的MEMORY部分)
F28335 的物理内存主要包括:片内 RAM 、片内 Flash 、OTP 、Boot ROM 、XINTF(外部接口) 。其中,Flash 是重点 ,因为Flash28_API就是操作它的。
(1) 片内 RAM(高速读写,用于运行代码 / 存储数据)
- RAMM0/RAMM1:低地址 RAM(0x000000 开始),通常用于堆栈、外设寄存器映射、小量数据。
- RAML0~RAML7:高地址 RAM(0x008000 开始),分为 8 个块(每块 4KB),是主要的用户 RAM,可用于运行代码(PAGE 0)或存储数据(PAGE 1)。
- 特点:访问速度快(0 等待状态),但掉电丢失数据。
(2) 片内 Flash(非易失性,用于存储程序 / 常量)
这是Flash28_API操作的核心对象,F28335 的片内 Flash 被划分为多个扇区(Sectors) (也叫 Blocks),每个扇区有独立的地址范围和大小,对应 cmd 文件中的FLASHH~FLASHA:
| Flash 扇区名称 | 起始地址(origin) | 长度(length) | 大小 | 备注 |
|---|---|---|---|---|
| FLASHH | 0x300000 | 0x008000 | 32KB | 最高地址的 Flash 扇区 |
| FLASHG | 0x308000 | 0x008000 | 32KB | |
| FLASHF | 0x310000 | 0x008000 | 32KB | |
| FLASHE | 0x318000 | 0x008000 | 32KB | |
| FLASHD | 0x320000 | 0x008000 | 32KB | 常用作 ramfuncs/Flash API 的存储区 |
| FLASHC | 0x328000 | 0x008000 | 32KB | |
| FLASHB | 0x330000 | 0x008000 | 32KB | |
| FLASHA | 0x338000 | 0x007F80 | ~32KB | 包含 CSM、启动向量等 |
Flash 扇区的关键特性:
- 擦除粒度 :Flash 只能按扇区擦除 (不能按字节擦除),比如擦除 FLASHA 必须擦除整个扇区,而
Flash28_API的核心功能之一就是实现扇区擦除。 - 编程限制:向 Flash 写入数据前,必须先擦除对应的扇区(擦除后扇区内容为 0xFFFF)。
- 执行冲突 :不能从正在被擦除 / 编程的 Flash 扇区中运行代码 (比如你在 FLASHA 中运行代码,同时尝试擦除 FLASHA,会导致总线错误)。这是
Flash28_API需要放到 RAM 中运行的根本原因。
(3) 其他内存(辅助了解)
- OTP:一次性可编程内存,用于存储加密密钥、校准数据。
- Boot ROM:固化了启动程序、IQ 数学表、FPU 表,不可修改。
- XINTF:外部内存接口,用于扩展外部 Flash/RAM。
二、cmd 文件的MEMORY部分:将硬件内存映射为链接器的逻辑块
了解了硬件内存后,再看 cmd 文件的MEMORY部分,就会发现它只是将 F28335 的物理内存地址和大小,定义为链接器能识别的逻辑名称 (比如FLASHD、RAML0)。
示例:MEMORY部分的核心代码解析
bash
MEMORY
{
PAGE 0: /* Program Memory:程序内存,对应哈佛架构的程序空间 */
RAML0 : origin = 0x008000, length = 0x001000 /* 物理RAML0的地址和大小 */
FLASHD : origin = 0x320000, length = 0x008000 /* 物理FlashD的地址和大小 */
FLASHA : origin = 0x338000, length = 0x007F80 /* 物理FlashA的地址和大小 */
// 其他程序内存块...
PAGE 1 : /* Data Memory:数据内存,对应哈佛架构的数据空间 */
RAMM1 : origin = 0x000400, length = 0x000400 /* 物理RAMM1的地址和大小 */
RAML4 : origin = 0x00C000, length = 0x001000 /* 物理RAML4的地址和大小 */
// 其他数据内存块...
}
origin:物理内存的起始地址(对应硬件手册中的地址)。length:内存块的大小(十六进制)。- 逻辑名称 :比如
RAML0、FLASHD,是链接器的别名,方便后续分配段。
在 F28335 的链接器命令文件(.cmd)中,SECTIONS是和MEMORY并列的核心部分,两者是 **"逻辑段" 与 "物理内存" 的映射关系 **。我们可以用一个通俗的比喻来理解:
| 组件 | 类比 | 作用 |
|---|---|---|
MEMORY部分 |
一栋大楼的房间 | 定义了硬件的物理内存(Flash 扇区、RAM 块),每个 "房间" 有地址(origin)、大小(length)、分区(PAGE 0/1) |
SECTIONS部分 |
大楼里的人和物 | 定义了程序的各个 "逻辑段"(代码、数据、常量、堆栈等),并指定每个 "人 / 物" 要放进哪个 "房间" 里 |
2. SECTIONS的两个核心概念
理解这两个概念,就能看懂所有段配置的逻辑:
(1) 段(Section):程序的 "逻辑模块"
段是程序的最小逻辑单元,分为两类:
- 编译器默认段 :由 C/C++ 编译器自动生成的段,是固定名称的(比如
.text、.cinit、.ebss、.econst),每个段有固定的功能(比如.text存代码,.ebss存未初始化变量)。 - 用户自定义段 :用户根据需求创建的段(比如
ramfuncs、Flash28_API、DMARAML4),用于将特定功能的代码 / 数据整合在一起,方便统一分配内存。
(2) 段的内存属性配置:解决硬件限制的关键
链接器允许为段配置存储地址(LOAD)和运行地址(RUN),这是 F28335 解决硬件限制的核心语法:
LOAD(加载地址 / 存储地址) :程序被烧录到硬件时,该段的物理存储位置(通常是 Flash,非易失,掉电不丢)。RUN(运行地址) :程序运行时,该段实际在内存中执行 / 存储的位置(通常是 RAM,高速,无硬件冲突)。- 两种模式 :
LOAD=RUN(默认) :存储地址和运行地址相同,段直接在存储位置运行(比如.text段存在 FLASHA,直接在 FLASHA 中运行)。LOAD≠RUN(分离模式) :存储地址和运行地址不同,需要手动将段从 LOAD 地址复制到 RUN 地址后,才能运行 (这是ramfuncs、Flash28_API的核心设计)。
二、Flash API 是什么(先说清本质)
Flash28335_API_V210.lib 是什么?
它是 TI 官方提供的 Flash 操作库,内部包含:
-
Flash_Erase() -
Flash_Program() -
Flash_Verify() -
各种状态机、超时、校验逻辑
⚠️ 重点:
-
这些函数 绝对不能在 Flash 中执行
-
必须在 SARAM(RAM)中运行
三、cmd 中 Flash API 的"总体结构"
我们从这段开始:
Flash28_API: { -lFlash28335_API_V210.lib(.econst) -lFlash28335_API_V210.lib(.text) } LOAD = FLASHD, RUN = RAML0, LOAD_START(_Flash28_API_LoadStart), LOAD_END(_Flash28_API_LoadEnd), RUN_START(_Flash28_API_RunStart), PAGE = 0
接下来 逐字符级拆解。
四、Flash28_API: ------ 这是在定义什么?
Flash28_API:
含义
👉 定义一个 section 名字
-
section 名:
Flash28_API -
不是 C 变量
-
不是函数
-
是链接器内部的逻辑容器
你可以把它理解成:
"把某些目标文件里的某些段,打包成一个整体"
五、花括号 {} 里面:真正装了什么?
{ -lFlash28335_API_V210.lib(.econst) -lFlash28335_API_V210.lib(.text) }
1️⃣ -lFlash28335_API_V210.lib
-
-l= link library -
指定一个库文件
-
等价于在 CCS 里把这个 lib 加入工程
2️⃣ (.text) / (.econst)
这是精华中的精华
.text
-
Flash API 的 函数代码
-
机器指令
.econst
-
Flash API 用到的:
-
常量
-
查表
-
参数表
-
👉 TI 非常严谨:
-
不只搬函数
-
连常量也一起搬进 RAM
3️⃣ 为什么不用 *(.text)?
因为:
只想要 Flash API 的代码
不想把你自己写的
.text一起拖进来
这是"精准抓取",不是"野蛮复制"。
六、LOAD = FLASHD ------ 编译后放哪?
LOAD = FLASHD
含义(非常重要)
链接完成后,这段代码在 FlashD 扇区
也就是说:
-
烧录文件(.out / .hex)
-
Flash API 实体代码
-
最初就写在 FLASHD
📌 FLASHD 的角色:
-
API 的"仓库"
-
上电后复制源
七、RUN = RAML0 ------ 真正执行在哪?
RUN = RAML0
含义
CPU 实际取指地址 = RAML0
也就是说:
-
Flash API 从不在 Flash 中运行
-
永远在 RAML0 中执行
📌 为什么是 RAML0?
-
4KB 连续
-
零等待
-
靠近 Flash
-
TI 官方推荐
八、LOAD_START / LOAD_END / RUN_START
LOAD_START(_Flash28_API_LoadStart), LOAD_END(_Flash28_API_LoadEnd), RUN_START(_Flash28_API_RunStart),
这三行是给谁用的?
👉 给 C 代码用的
链接器会自动生成三个符号:
| 符号 | 含义 |
|---|---|
_Flash28_API_LoadStart |
Flash 中起始地址 |
_Flash28_API_LoadEnd |
Flash 中结束地址 |
_Flash28_API_RunStart |
RAM 中目标地址 |
在 C 代码里你会看到类似:
extern Uint16 Flash28_API_LoadStart; extern Uint16 Flash28_API_LoadEnd; extern Uint16 Flash28_API_RunStart; memcpy(&Flash28_API_RunStart, &Flash28_API_LoadStart, (Uint32)&Flash28_API_LoadEnd - (Uint32)&Flash28_API_LoadStart);
📌 这一步 = "灵魂搬家"
九、PAGE = 0 是干嘛的?
PAGE = 0
含义
-
PAGE0 = 程序空间
-
这是 取指空间
-
Flash API 是"程序代码",不是数据
👉 即使它运行在 RAM,也仍然属于程序段
十、ramfuncs:和 Flash API 的关系
ramfuncs LOAD = FLASHD RUN = RAML0
本质关系:
| 项目 | Flash28_API | ramfuncs |
|---|---|---|
| 来源 | TI 官方 | 你自己 |
| 功能 | 擦写 Flash | 任意高优先级 |
| 放哪 | FLASHD → RAML0 | FLASHD → RAML0 |
| 本质 | 完全一样 |
👉 在链接器眼里,它们是同一类生物
十一、为什么 Flash API 单独占一个 section?
如果不这么做,会发生什么?
❌ 错误做法
-
把 Flash API 混进
.text -
.text全放 FLASHA
❌ 后果
-
Flash API 在 FLASHA
-
你擦别的扇区时
-
CPU 正在 Flash 里取指
-
系统死机 / 跑飞
👉 所以 TI 强制隔离
十二、Flash API 与"8 个扇区"的工程关系总结
| 扇区 | 用途 |
|---|---|
| FLASHA | 主程序 + Boot |
| FLASHD | Flash API / ramfuncs 装载区 |
| FLASHC | 数学库 |
| FLASHB / E / F / G / H | 用户数据 |
十三、逐字符速查表(给你当手册用)
| 符号 | 含义 |
|---|---|
Flash28_API: |
定义 section |
{} |
section 内容 |
-lxxx.lib |
链接库 |
(.text) |
函数代码 |
(.econst) |
常量 |
LOAD = |
编译后存放地址 |
RUN = |
实际执行地址 |
LOAD_START() |
Flash 源地址 |
RUN_START() |
RAM 目标地址 |
PAGE = 0 |
程序空间 |
1. .cinit : > FLASHA PAGE=0
-
作用 :存储 C 语言初始化数据(
.cinit是编译器生成的常量初始化表) -
存储位置 :
FLASHA(Flash 扇区,非易失) -
逻辑:
-
这些数据在程序启动时会被复制到 RAM 中对应的变量区域
-
.cinit是链接器自动生成的,通常包含全局/静态变量的初始值
-
-
为什么 PAGE=0:
-
Flash 是程序内存,PAGE 0 对应程序存储空间
-
代码启动时读取
.cinit数据
-
2. .pinit : > FLASHA, PAGE=0
-
作用 :存储 C++ 全局对象或类的构造函数初始化表(
.pinit是初始化函数指针表) -
存储位置 :
FLASHA -
逻辑:
-
程序启动时,C++ 对象构造函数通过
.pinit指针表调用 -
这样可以确保全局对象在 main 之前正确初始化
-
-
为什么 PAGE=0:同样是程序内存,FLASH 存储,不会丢失
3. .text : > FLASHA PAGE=0
-
作用:存储普通程序代码(text section)
-
存储位置 :
FLASHA -
逻辑:
-
程序的主函数、用户函数等都放在
.text -
FLASH 非易失,掉电不丢失
-
PAGE=0,因为是程序空间
-
4. codestart : > BEGIN PAGE=0
-
作用:定义程序入口地址
-
存储位置 :
BEGIN(FLASH 中极小地址,用于 bootloader 或启动向量) -
逻辑:
-
CPU 上电或复位时,从这个地址开始执行
_c_int00初始化程序 -
PAGE=0,因为是程序入口
-
5. ramfuncs : LOAD=FLASHD RUN=RAML0 ... PAGE=0
-
作用:存储用户高速函数(RAM 执行)
-
配置解释:
-
LOAD=FLASHD→ 编译器/链接器把代码存储在 FLASHD 扇区 -
RUN=RAML0→ 程序运行时把这些函数复制到 RAML0 执行 -
_RamfuncsLoadStart/_RamfuncsLoadEnd→ FLASHD 中函数的起止地址 -
_RamfuncsRunStart→ RAML0 中运行起始地址 -
PAGE=0 → 属于程序内存空间
-
-
为什么要这么做:
-
避免从 Flash 中运行高速函数或在操作 Flash 时造成冲突
-
类似 Flash28_API 的"Flash存储,RAM运行"方案
完整的cmd文件:
bash/* //########################################################################### // // FILE: F28335.cmd // // TITLE: Linker Command File For F28335 Device // //########################################################################### // $TI Release: F28335 API Release V2.10 $ // $Release Date: August 18, 2008 $ //########################################################################### */ /* ====================================================== // For Code Composer Studio V2.2 and later // --------------------------------------- // In addition to this memory linker command file, // add the header linker command file directly to the project. // The header linker command file is required to link the // peripheral structures to the proper locations within // the memory map. // // The header linker files are found in <base>\DSP2833x_Headers\cmd // // For BIOS applications add: DSP2833x_Headers_BIOS.cmd // For nonBIOS applications add: DSP2833x_Headers_nonBIOS.cmd ========================================================= */ /* ====================================================== // For Code Composer Studio prior to V2.2 // -------------------------------------- // 1) Use one of the following -l statements to include the // header linker command file in the project. The header linker // file is required to link the peripheral structures to the proper // locations within the memory map */ /* Uncomment this line to include file only for non-BIOS applications */ /* -l DSP2833x_Headers_nonBIOS.cmd */ /* Uncomment this line to include file only for BIOS applications */ /* -l DSP2833x_Headers_BIOS.cmd */ /* 2) In your project add the path to <base>\DSP2833x_headers\cmd to the library search path under project->build options, linker tab, library search path (-i). /*========================================================= */ /* Define the memory block start/length for the F28335 PAGE 0 will be used to organize program sections PAGE 1 will be used to organize data sections Notes: Memory blocks on F28335 are uniform (ie same physical memory) in both PAGE 0 and PAGE 1. That is the same memory region should not be defined for both PAGE 0 and PAGE 1. Doing so will result in corruption of program and/or data. L0/L1/L2 and L3 memory blocks are mirrored - that is they can be accessed in high memory or low memory. For simplicity only one instance is used in this linker file. Contiguous SARAM memory blocks can be combined if required to create a larger memory block. */ MEMORY { PAGE 0: /* Program Memory */ /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE1 for data allocation */ ZONE0 : origin = 0x004000, length = 0x001000 /* XINTF zone 0 */ RAML0 : origin = 0x008000, length = 0x001000 /* on-chip RAM block L0 */ RAML1 : origin = 0x009000, length = 0x001000 /* on-chip RAM block L1 */ RAML2 : origin = 0x00A000, length = 0x001000 /* on-chip RAM block L2 */ RAML3 : origin = 0x00B000, length = 0x001000 /* on-chip RAM block L3 */ ZONE6 : origin = 0x0100000, length = 0x100000 /* XINTF zone 6 */ ZONE7A : origin = 0x0200000, length = 0x00FC00 /* XINTF zone 7 - program space */ FLASHH : origin = 0x300000, length = 0x008000 /* on-chip FLASH */ FLASHG : origin = 0x308000, length = 0x008000 /* on-chip FLASH */ FLASHF : origin = 0x310000, length = 0x008000 /* on-chip FLASH */ FLASHE : origin = 0x318000, length = 0x008000 /* on-chip FLASH */ FLASHD : origin = 0x320000, length = 0x008000 /* on-chip FLASH */ FLASHC : origin = 0x328000, length = 0x008000 /* on-chip FLASH */ FLASHA : origin = 0x338000, length = 0x007F80 /* on-chip FLASH */ CSM_RSVD : origin = 0x33FF80, length = 0x000076 /* Part of FLASHA. Program with all 0x0000 when CSM is in use. */ BEGIN : origin = 0x33FFF6, length = 0x000002 /* Part of FLASHA. Used for "boot to Flash" bootloader mode. */ CSM_PWL : origin = 0x33FFF8, length = 0x000008 /* Part of FLASHA. CSM password locations in FLASHA */ OTP : origin = 0x380400, length = 0x000400 /* on-chip OTP */ ADC_CAL : origin = 0x380080, length = 0x000009 /* ADC_cal function in Reserved memory */ IQTABLES : origin = 0x3FE000, length = 0x000b50 /* IQ Math Tables in Boot ROM */ IQTABLES2 : origin = 0x3FEB50, length = 0x00008c /* IQ Math Tables in Boot ROM */ FPUTABLES : origin = 0x3FEBDC, length = 0x0006A0 /* FPU Tables in Boot ROM */ ROM : origin = 0x3FF27C, length = 0x000D44 /* Boot ROM */ RESET : origin = 0x3FFFC0, length = 0x000002 /* part of boot ROM */ VECTORS : origin = 0x3FFFC2, length = 0x00003E /* part of boot ROM */ PAGE 1 : /* Data Memory */ /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE0 for program allocation */ /* Registers remain on PAGE1 */ BOOT_RSVD : origin = 0x000000, length = 0x000050 /* Part of M0, BOOT rom will use this for stack */ RAMM0 : origin = 0x000050, length = 0x0003B0 /* on-chip RAM block M0 */ RAMM1 : origin = 0x000400, length = 0x000400 /* on-chip RAM block M1 */ RAML4 : origin = 0x00C000, length = 0x001000 /* on-chip RAM block L1 */ RAML5 : origin = 0x00D000, length = 0x001000 /* on-chip RAM block L1 */ RAML6 : origin = 0x00E000, length = 0x001000 /* on-chip RAM block L1 */ RAML7 : origin = 0x00F000, length = 0x001000 /* on-chip RAM block L1 */ ZONE7B : origin = 0x20FC00, length = 0x000400 /* XINTF zone 7 - data space */ FLASHB : origin = 0x330000, length = 0x008000 /* on-chip FLASH */ } /* Allocate sections to memory blocks. Note: codestart user defined section in DSP28_CodeStartBranch.asm used to redirect code execution when booting to flash ramfuncs user defined section to store functions that will be copied from Flash into RAM */ SECTIONS { /* Allocate program areas: */ /* The Flash API functions can be grouped together as shown below. The defined symbols _Flash28_API_LoadStart, _Flash28_API_LoadEnd and _Flash28_API_RunStart are used to copy the API functions out of flash memory and into SARAM */ Flash28_API: { -lFlash28335_API_V210.lib(.econst) -lFlash28335_API_V210.lib(.text) } LOAD = FLASHD, RUN = RAML0, LOAD_START(_Flash28_API_LoadStart), LOAD_END(_Flash28_API_LoadEnd), RUN_START(_Flash28_API_RunStart), PAGE = 0 .cinit : > FLASHA PAGE = 0 .pinit : > FLASHA, PAGE = 0 .text : > FLASHA PAGE = 0 codestart : > BEGIN PAGE = 0 ramfuncs : LOAD = FLASHD, RUN = RAML0, LOAD_START(_RamfuncsLoadStart), LOAD_END(_RamfuncsLoadEnd), RUN_START(_RamfuncsRunStart), PAGE = 0 csmpasswds : > CSM_PWL PAGE = 0 csm_rsvd : > CSM_RSVD PAGE = 0 /* Allocate uninitalized data sections: */ .stack : > RAMM1 PAGE = 1 .ebss : > RAML4 PAGE = 1 .esysmem : > RAMM1 PAGE = 1 /* Initalized sections go in Flash */ /* For SDFlash to program these, they must be allocated to page 0 */ .econst : > FLASHA PAGE = 0 .switch : > FLASHA PAGE = 0 /* Allocate IQ math areas: */ IQmath : > FLASHC PAGE = 0 /* Math Code */ IQmathTables : > IQTABLES, PAGE = 0, TYPE = NOLOAD IQmathTables2 : > IQTABLES2, PAGE = 0, TYPE = NOLOAD FPUmathTables : > FPUTABLES, PAGE = 0, TYPE = NOLOAD /* Allocate DMA-accessible RAM sections: */ DMARAML4 : > RAML4, PAGE = 1 DMARAML5 : > RAML5, PAGE = 1 DMARAML6 : > RAML6, PAGE = 1 DMARAML7 : > RAML7, PAGE = 1 /* Allocate 0x400 of XINTF Zone 7 to storing data */ ZONE7DATA : > ZONE7B, PAGE = 1 /* .reset is a standard section used by the compiler. It contains the */ /* the address of the start of _c_int00 for C Code. /* /* When using the boot ROM this section and the CPU vector */ /* table is not needed. Thus the default type is set here to */ /* DSECT */ .reset : > RESET, PAGE = 0, TYPE = DSECT vectors : > VECTORS PAGE = 0, TYPE = DSECT /* Allocate ADC_cal function (pre-programmed by factory into TI reserved memory) */ .adc_cal : load = ADC_CAL, PAGE = 0, TYPE = NOLOAD } /* //=========================================================================== // End of file. //=========================================================================== */
-