将 ADC 采样逻辑从 CPU 中断迁移到 CLA Task1 时,遇到一个隐蔽的问题:无论怎么触发,共享变量始终是 0,CLA 没有运行。本文记录完整的排查过程和两种可行方案。
结论
LS0~LS7 中任意一块做 CLA program memory(如 LS2),另一块做 CLA 数据区(如 LS7)。初始化顺序和 memcpy 写法都是标准的,无需特殊处理。这是推荐方案。
LS8/LS9 做 CLA program memory 同样可行,但需同时处理两个问题:memcpy 目标地址要加 0x10000U 偏移,且 memcpy 必须在 MEMCFG_init() 之前执行。
核心区别:
- LS0~LS7 做 CLA program:普通 memcpy,SysConfig 自动生成代码可直接使用。
- LS8/LS9 做 CLA program:memcpy 地址加
0x10000U,且必须在 MEMCFG 配置 LS8 之前执行。
方案一:LS2 + LS7(推荐)
SysConfig 配置
<!-- 图1:SysConfig MEMCFG 配置截图,LS2=CLA program memory,LS7=CPU/CLA shared data memory -->
LS2 RAM → CLA program memory
LS7 RAM → CPU/CLA shared data memory
linker cmd
LS0~LS7 的 CPU 地址和 CLA 地址相同,不存在双映射问题。以 LS2 为例:
RAMLS2 : origin = 0x009000, length = 0x000800
RAMLS7 : origin = 0x00B800, length = 0x000800
Cla1Prog :
{
*(Cla1Prog)
} LOAD = FLASH_BANK1,
RUN = RAMLS2,
LOAD_START(Cla1ProgLoadStart),
LOAD_SIZE(Cla1ProgLoadSize),
RUN_START(Cla1ProgRunStart),
ALIGN(8)
.const_cla : LOAD = FLASH_BANK1,
RUN = RAMLS7,
LOAD_START(Cla1ConstLoadStart),
LOAD_SIZE(Cla1ConstLoadSize),
RUN_START(Cla1ConstRunStart),
ALIGN(8)
ClaSharedRam : > RAMLS7, ALIGN(4)
.scratchpad : > RAMLS7, ALIGN(4)
.bss_cla : > RAMLS7, ALIGN(4)
Flash 工程的 memcpy
LS2 方案不需要地址偏移:
memcpy((uint32_t *)&Cla1ProgRunStart,
(uint32_t *)&Cla1ProgLoadStart,
(uint32_t)&Cla1ProgLoadSize);
初始化顺序
LS2 不存在地址双映射问题,SysConfig 自动生成的 Board_init() 顺序(先 CLA_init() 再 MEMCFG_init())对该方案适用,无需调整。
方案二:LS8 + LS7(进阶)
LS8 与 LS2 的区别
F28P55x 的 LS8/LS9 在物理上位于高地址,超出 CLA 地址总线的 16-bit 范围。TI 通过硬件重映射,使 CLA 能够通过低地址访问:
<!-- 图2:LS8 地址双映射示意图,左边 CPU 视角 0x14000,右边 CLA 视角 0x4000,中间标注偏移 0x10000 -->
CPU 视角:LS8 = 0x014000
CLA 视角:LS8 = 0x004000(硬件重映射)
偏移量 :0x010000
LS0~LS7 没有这个问题,CPU 和 CLA 看到同一个地址。
SysConfig 配置
<!-- 图3:SysConfig MEMCFG 配置截图,LS7=CPU/CLA shared data memory,LS8=CLA program memory -->
LS7 RAM → CPU/CLA shared data memory
LS8 RAM → CLA program memory
自动生成代码在 LS8 场景下的两个问题
SysConfig 生成的 Board_init() 处理 Flash 工程时,先执行 CLA_init()(内含 memcpy),再执行 MEMCFG_init()。该顺序对 LS2 合理,但用于 LS8 时存在两个必须同时解决的问题:
问题一,地址偏移缺失。自动生成的 memcpy 直接以 &Cla1ProgRunStart 为目标地址,而 linker 中 RAMLS8_CLA origin = 0x004000,因此 Cla1ProgRunStart 的值是 CLA 视角的 0x4000,并非 CPU 能写入的 0x14000。缺少 0x10000 偏移,程序被拷贝到错误地址,CLA 取指时得到空内容。
问题二,配置顺序。LS8 一旦被 MEMCFG_init() 配置为 CLA program memory,CPU 便无法再写入。若按自动生成的顺序先配置后拷贝,memcpy 会静默失败,结果与问题一相同。
两个问题缺一不可。正确顺序为:先 memcpy(含偏移),再 MEMCFG 配置 LS8,最后初始化 CLA Task。
<!-- 图4:初始化顺序对比,左侧错误:MEMCFG→memcpy→CLA Task init,右侧正确:memcpy→MEMCFG→CLA Task init -->
不能修改 SysConfig 生成的文件
CLA_init() 与 MEMCFG_init() 都位于 SysConfig 生成的 board.c 中,该文件在每次编译时被重新生成并覆盖,因此不能直接修改 board.c,任何改动都会在下次编译时丢失。
正确做法是在 main.c 中重写 CLA 的初始化逻辑:自己实现拷贝函数(带 0x10000U 偏移),不调用 Board_init(),改为手动按正确顺序调用各初始化函数。MEMCFG_init() 和 myCLA0_init() 可继续复用 SysConfig 生成的版本,关键在于调用顺序由 main.c 控制。
linker cmd
RAMLS7 : origin = 0x00B800, length = 0x000800
RAMLS8_CLA : origin = 0x004000, length = 0x002000 /* CLA 视角地址 */
Cla1Prog :
{
*(Cla1Prog)
} LOAD = FLASH_BANK1,
RUN = RAMLS8_CLA,
LOAD_START(Cla1ProgLoadStart),
LOAD_SIZE(Cla1ProgLoadSize),
RUN_START(Cla1ProgRunStart),
ALIGN(8)
.const_cla : LOAD = FLASH_BANK1,
RUN = RAMLS7,
LOAD_START(Cla1ConstLoadStart),
LOAD_SIZE(Cla1ConstLoadSize),
RUN_START(Cla1ConstRunStart),
ALIGN(8)
ClaSharedRam : > RAMLS7, ALIGN(4)
.scratchpad : > RAMLS7, ALIGN(4)
.bss_cla : > RAMLS7, ALIGN(4)
在 main.c 中重写拷贝函数
在 main.c 中定义一个仅负责拷贝的函数,目标地址加 0x10000U 偏移。注意此函数只做拷贝,不包含 myCLA0_init(),以便与内存配置、Task 初始化在 main() 中按正确顺序分开调用:
static void CLA_copyProgram(void)
{
extern uint32_t Cla1ProgRunStart;
extern uint32_t Cla1ProgLoadStart;
extern uint32_t Cla1ProgLoadSize;
extern uint32_t Cla1ConstRunStart;
extern uint32_t Cla1ConstLoadStart;
extern uint32_t Cla1ConstLoadSize;
/* Cla1ProgRunStart = 0x4000(CLA视角),CPU 写 LS8 需要 0x14000 */
memcpy((uint32_t *)((uint32_t)&Cla1ProgRunStart + 0x10000U),
(uint32_t *)&Cla1ProgLoadStart,
(uint32_t)&Cla1ProgLoadSize);
/* .const_cla 放在 LS7,CPU/CLA 地址相同,不需要偏移 */
memcpy((uint32_t *)&Cla1ConstRunStart,
(uint32_t *)&Cla1ConstLoadStart,
(uint32_t)&Cla1ConstLoadSize);
}
main() 中的初始化顺序
不调用 Board_init(),改为手动按顺序调用,将拷贝置于 MEMCFG_init() 之前:
EALLOW;
PinMux_init();
SYNC_init();
ASYSCTL_init();
CLA_copyProgram(); /* 步骤一:拷贝 CLA 程序到 LS8(含偏移),此时 LS8 仍为普通 RAM,CPU 可写 */
MEMCFG_init(); /* 步骤二:将 LS8 配置为 CLA program memory */
myCLA0_init(); /* 步骤三:映射 Task 向量、设置触发源、使能 Task1 */
ADC_init();
CPUTIMER_init();
EPWM_init();
GPIO_init();
INTERRUPT_init();
EDIS;
CLA 相关三个文件
cla_shared.h / cla_shared.c
共享变量必须定义在 CPU 侧的 .c 文件中,通过 DATA_SECTION 放入共享 RAM,不能定义在 .cla 文件里,两侧数据页寻址方式不同:
/* cla_shared.c */
#pragma DATA_SECTION(g_adcA0Raw, "ClaSharedRam");
volatile uint16_t g_adcA0Raw = 0U;
#pragma DATA_SECTION(g_adcB0Raw, "ClaSharedRam");
volatile uint16_t g_adcB0Raw = 0U;
/* cla_shared.h */
extern volatile uint16_t g_adcA0Raw;
extern volatile uint16_t g_adcB0Raw;
类型使用 uint16_t / int16_t 等固定位宽类型,避免使用 int / unsigned int。C28x 上 int 为 16-bit,跨核传递数据时容易出现隐式截断。
cla_tasks.cla
调试阶段先写固定值,验证 CLA 能运行、共享内存可写通:
#pragma CODE_SECTION(Cla1Task1, "Cla1Prog");
__interrupt void Cla1Task1(void)
{
g_adcA0Raw = 111;
g_adcB0Raw = 222;
g_adcC0Raw = 333;
g_adcD0Raw = 444;
HWREGH(ADCA_BASE + ADC_O_INTFLGCLR) = 0x0001U;
}
Watch 窗口确认变量为 111/222/333/444 后,替换为真实的 ADC 结果读取:
g_adcA0Raw = HWREGH(ADCA_BASE + ADC_O_RESULT0);
踩坑过程记录
坑一:LS2 配成 data 而非 program
SysConfig 中 LS2 的默认选项是 CPU/CLA shared data memory,并非 CLA program memory。data memory 不能被 CLA 取指,Task1 不会执行。
修复:SysConfig → MEMCFG → LS2 → 改为 CLA program memory。
坑二:CLA_init 在 MEMCFG_init 之前(LS2 场景)
SysConfig 自动生成的 Board_init() 顺序为先 CLA_init() 再 MEMCFG_init()。对 LS2 而言此顺序适用,当时误判为问题来源,走了弯路。
坑三:改用 LS8 后 memcpy 仍写不进去
换到 LS8 后,在 CCS 中单步执行完 memcpy,立即查看 Memory Browser 的 0x014000,内容仍为全 0。
根本原因是前述两个问题同时存在:地址未加偏移,且此时 MEMCFG 已将 LS8 配为 CLA program memory,CPU 写不进去。在 TI E2E 上确认 TI 官方 cla_asin_ls8_9 示例的流程是先 memcpy 再配置 LS8,偏移同样不可省略。按此方案修改后,0x014000 出现指令数据,CLA_forceTasks 触发后变量正确变为 111/222/333/444。
坑四:触发源配置
SysConfig 中 CLA Task1 的 Trigger Source 选 ADCA1 时,需要 ADCA 的 ADCINT1 使能且 EPWM SOC 正常工作。调试阶段建议先改为 Software,用 CLA_forceTasks 手动触发,排除触发链路的干扰。
CLA 在 CCS 中的调试
CLA 是独立处理器核,需在 Debug 视图中单独 Connect:
- 暂停程序,在 Debug 视图找到
CLA1核,右键 Connect Target - 在
.cla源文件中正常打断点,CLA1 核会在断点处暂停 - Watch 窗口可直接查看共享变量,CPU 和 CLA 两侧都能读到当前值
CPU 核与 CLA 核的断点相互独立,互不影响。
参考
- TI E2E:F28P55x CLA LS8/LS9 SysConfig 配置讨论
- TI E2E:F28P65x LS8 CLA program not working(含官方 memcpy 偏移解释)
- C2000 CLA Software Development Guide
- TMS320F28P55x Technical Reference Manual (SPRUJ57)