记不清是第几次给C6678做固件烧写了但这块板子最初给我留下的印象实在深刻8个C66x内核、主频1GHz跑起来性能确实猛算法在CCS里联调也一切正常可产品要交付时发现一个致命问题——一断电程序就没了。每次上电都得重新连仿真器、加载.out文件产线那边根本没法接受。TMS320C6678作为一颗多核DSP内部不集成Flash程序默认只能通过仿真器加载到片内SRAM或DDR3中运行。想要做到上电自启动就必须走完整的“程序固化”流程把编译好的应用程序转换成合适的镜像格式烧写进外部SPI Flash或NAND Flash并把DSP的引导模式配置成对应启动方式。这篇文章记录我完成C6678单核程序烧写的全过程包含工具链配合方式、关键寄存器配置、镜像格式转换以及烧写后启动失败时的排查思路。1. 先搞清楚现状为什么你的C6678一断电就“失忆”很多从单片机转过来的人第一反应是把.out文件直接丢进Flash然后盼望DSP上电能自己跑起来。这个做法在C6678上行不通你得先理解在线调试和Flash自启动之间的本质区别。1.1 在线调试与Flash自启动的本质差别在线调试时CCS通过JTAG仿真器XDS100V2、XDS200或XDS560V2把你的.out文件逐段加载到DSP的L2 SRAM或DDR3中然后设置好PC指针DSP开始执行。整个过程有仿真器“扶着”CCS会做地址映射、段加载、甚至提供GEL初始化。但脱机运行时这些辅助手段统统不存在。DSP上电后唯一能执行的是固化在片内ROM里的引导代码也就是RBLROM Boot Loader。RBL的职责很简单根据外部引脚状态确定引导模式从对应的外部存储介质读取镜像放到内存里最后跳转执行。整个过程中没有任何人可以替你做DDR3初始化也没有调试器帮你设置PC指针。所以烧写Flash的本质不是你“存了一个文件进去”而是你精心构造了一份“RBL能读得懂、加载得起来”的镜像并让它落在正确的位置。这也是整个烧写过程最核心的认知门槛。1.2 RBL到底帮你做了什么C6678上电后RBL的执行流程大致如下等待电源稳定、时钟锁定内部PLL被配置到固定频率。采样DEVSTAT引脚的电平状态得到BOOTMODE值。根据BOOTMODE判断启动介质SPI、NAND、NOR、EMIF16、PCIe、SRIO等。通过对应接口把镜像从外部存储设备读到芯片内部SRAM地址0x00800000L2 SRAM。校验镜像格式Magic Word/头信息如果合法就跳转执行。注意第4步RBL默认把镜像加载到0x00800000也就是L2 SRAM区域。C6678每核有1MB L2可配置为SRAM或Cache。如果你的程序很大L2放不下RBL本身不会好心帮你初始化DDR3然后把程序搬过去——它没有DDR3初始化代码。这就是后续所有“烧写成功但启动失败”问题的根源后面我会专门展开。1.3 .out文件、.dat镜像和最终烧进Flash的内容搞清楚烧写目标前必须区分三种文件格式。格式本质能不能直接烧写.outELF格式包含段信息、符号表、调试信息不能RBL无法解析ELF.datTI通用数据格式由地址长度数据记录组成能配合烧写工具写入Flash.bin/.hex纯二进制或不同厂家的hex格式能通常配合二次Bootloader使用Boot Table镜像TI自定义的启动表格式含段头地址、长度、数据最佳选择RBL原生支持实际开发中我最常用的组合是用hex6x工具把.out转换成带Boot Table的hex/image再通过CCS的Load Memory功能以.dat方式加载到DSP内存缓冲区最后由Flash Writer写入SPI Flash。这个过程看起来绕但每一步都有明确目的。2. 烧写前最容易被忽略的一步GEL与DDR初始化很多人在烧写流程里卡住不是因为最后一步写Flash的命令不对而是从一开始就没把DSP环境准备好。对C6678来说环境准备的核心就是DDR3初始化。2.1 硬件连接和仿真器选择烧写C6678需要仿真器推荐XDS200或XDS560V2。XDS100V2也能用但连接速度和稳定性稍差尤其在烧写大数据量镜像时可能出现超时。连接方式上注意JTAG排线的信号完整性和供电能力14pin接口标准个别自研板卡可能需要单独给仿真器接口供电。目标板通电后先确认几个关键电压内核电压1.0V、DDR3电压1.5V也有板卡用1.35V的低压DDR3L、IO电压1.8V/3.3V。如果电压不对GEL文件执行到DDR配置阶段就会挂掉。2.2 GEL文件里的DDR3时序参数GEL文件是CCS用来初始化DSP的脚本C6678的GEL文件中最重要的是DDR3控制器配置。TI原厂EVM板的GEL文件对应的是EVM板上的DDR3颗粒和时钟方案如果你的板卡是自研的直接套用原厂GEL大概率会出问题。我在一块自研板卡上就遇到过DDR3频率配置成1333MT/s但板卡实际走线质量和颗粒等级只能稳定跑800MT/s。结果就是在线调试时程序加载到DDR里偶尔报错烧写完成后启动更是一点反应都没有。GEL中需要核对的DDR3参数集中在DDR3_EMIF_Config结构体里DDR3频率DDR3_FREQ_800_MT等枚举值CL、CWL、tRCD、tRP等时序参数Bank数、Row数、Column数取决于DDR3芯片容量Burst长度和数据总线宽度提示验证GEL里DDR配置是否正确的方法很简单连接仿真器执行完GEL初始化后在CCS的Memory Browser里向DDR3区域比如0x80000000写入一串0x5A5A5A5A再读回来比对。如果读写一致且连续读写几百次不报错DDR基本没问题。2.3 烧写工具的两种选择准备烧写前先想好用哪种工具写入Flash。我在C6678上用过两种方案方案一TI官方Flash WriterStarterWare/Processor SDK中提供TI在StarterWare中提供了针对不同存储介质的Writer工程比如SPI Flash Writer、NAND Flash Writer。使用方法是把Writer的.out通过仿真器加载到DSP中运行然后在CCS里通过Load Memory把应用程序镜像加载到DDR缓冲区Writer再从缓冲区读数据写入Flash。优点是免费、可靠、支持校验缺点是要自己编译工程环境配置有点折腾。方案二第三方Flash烧写工具市面上有一些通过串口或网口把镜像写入SPI Flash的小工具。它们通常自带GUI使用更简单但需要根据你的板卡定制Flash型号和引脚配置兼容性不一定好。我个人建议第一次做C6678烧写时用方案一因为你能在CCS里看到每一步的状态更容易理解烧写原理。等流程跑通了再考虑用更顺手的方式提高效率。3. C6678单核烧写的完整流程以SPI Flash为例下面以最常见的SPI NOR Flash为例走一遍完整流程。我假设你的目标板通过SPI接口挂了一片NOR Flash比如常见的N25Q128或M25P64仿真器连接正常CCS版本建议5.5以上我用的CCS 6.2实测稳定。3.1 初始化目标板确认DDR可用第一步在CCS中新建或导入目标配置Target Configuration。选择芯片型号TMS320C6678仿真器型号根据实际设备选择。连接前确保已经给板卡上电。连接后CCS会自动执行GEL文件中的StartUp()函数完成PLL、DDR3等初始化。先用Memory Browser验证DDR3读写正常方法见2.2节然后确认L2 SRAM区域可访问。这一步做到位后面加载Writer和镜像才安全。3.2 加载并运行Flash Writer编译SPI Flash Writer工程得到writer.out。在CCS里通过Run - Load Program加载writer.out到DSP然后让它全速运行。Writer运行后会初始化SPI控制器和Flash芯片通常打印Flash ID或制造商ID。顺利的话你能在CCS的Console窗口看到类似Flash ID的打印信息。如果没有输出检查SPI引脚配置和Flash供电最常见的错误是CS片选引脚和板卡原理图对不上。3.3 生成应用程序镜像并写入Flash这是核心步骤。先处理好你的应用程序app.out下一步是把它转成烧写用的镜像格式。最省事的方式是使用TI的hex6x工具hex6x --boot --bootorg 0x00800000 --bootsection .text --bootsection .cinit app.out实际工程中我会写一个.cmd文件来定义hex转换规则app.out --boot --bootorg 0x00800000 --boot_sections .text,.cinit,.const --boot_table --image --spi -o app_bt.img注意这里的--bootorg是RBL跳转的入口地址。如果你用Boot Table方式--boot_table会生成带段头信息的镜像RBL按段头把各个段放到指定地址。生成镜像后用CCS的Tools - Load Memory将镜像加载到DDR中的一个安全缓冲区地址比如0x90000000。需要确保这个地址不会和Writer自身的数据区冲突同时Flash的最小擦除单元是扇区缓冲区大小要对齐。然后在Writer中执行擦除、烧写、校验三个动作擦除整个Flash或指定扇区从缓冲区把镜像写入FlashWriter会按照SPI协议发送页编程命令读回Flash内容与缓冲区内容比对确认一致整个烧写过程根据镜像大小耗时不同写入128KB镜像通常需要十几秒到几分钟耐心等待即可。3.4 设置SPI Boot模式并验证自启动烧写完成后断电把引导模式配置为SPI Boot。C6678的引导模式由DEVSTAT引脚决定EVM板上通常有拨码开关比如SW1。SPI Boot的典型配置是BOOTMODE[12:0]设为SPI Boot的对应值同时确认LENDIAN为小端模式C6678常用小端。重新上电后观察现象。最直接的验证方式是连接仿真器复位后看PC指针是否停在0x00800000附近Boot Table加载完成后的跳转入口或者程序运行后GPIO输出是否有变化。如果程序里串口打印初始化了可以在串口工具上观察是否有启动信息输出。实测时我习惯同时做两件事用示波器抓SPI Flash片选信号看复位后RBL是否去访问Flash再用仿真器挂上读PC寄存器判断执行位置。4. 烧写不启动九成卡在这几个点上如果你严格按照上面的流程操作大概率一次成功。但如果没启动也别急以下四个点几乎覆盖了我遇到过的所有坑。4.1 引导模式引脚配置的细节DEVSTAT引脚的采样发生在复位释放的时刻而且不是简单的拨一下开关就行。很多板卡上的拨码开关需要通过上拉/下拉电阻形成确定的电平如果电阻阻值选得不对引脚可能浮空RBL采到的就是随机值。我在一块板卡上吃过亏板卡画板时SPI Boot相关引脚的下拉电阻用了10kΩ但板子接的长排线带来了额外电容导致复位瞬间采样沿到来时电平还没稳定RBL偶尔识别成No Boot模式。后来把下拉电阻改成1kΩ才稳定。经验之谈遇到“上次能启动、这次不能”的间歇性故障优先怀疑Boot引脚电平稳定性用示波器量一下复位释放瞬间的电平建立时间。4.2 二次Bootloader加载主程序到DDR的唯一正规方法前面说过RBL默认只把镜像加载到0x00800000L2 SRAM不初始化DDR3。如果你的主程序比较大需要放到DDR里运行那你就必须走二次Bootloader路线。二次Bootloader的设计思路是这样的RBL把Bootloader从Flash加载到0x00800000并跳转。Bootloader本身做PLL、DDR3初始化再通过SPI从Flash的指定偏移比如0x10000读取主程序镜像。Bootloader把主程序镜像写入DDR3的指定地址然后跳转到主程序入口。Bootloader的代码不复杂核心逻辑可以用伪代码描述void main(void) { // 初始化PLL和DDR3 pll_init(); ddr3_init(); // 从SPI Flash 0x10000偏移读主程序到DDR 0x80000000 spi_flash_read(0x10000, DDR_BASE, APP_SIZE); // 跳转到主程序入口 ((void (*)())APP_ENTRY)(); }这个Bootloader本身要编译成Boot Table格式写到Flash偏移0x0主程序则编译成裸二进制镜像写到0x10000偏移。很多C6678工程实际用的是简化方案主程序不全部放在DDR只把初始化和中断向量放在L2 SRAM运行时需要的数据再放到DDR中由代码自行初始化。这种方式能省掉二次Bootloader但要确保RBL加载的镜像总大小不超过L2可用空间。C6678单核有1MB L2实际扣除Cache配置和堆栈后可用空间通常在几百KB级别。对于小算法够用大系统还是老老实实写Bootloader。4.3 镜像格式、Boot Table与地址对齐SPI Flash按页和扇区组织比如N25Q128的页256字节、扇区64KB写入时不能跨越页边界任意写。如果你的镜像数据位置和Flash物理页边界不对齐烧写工具必须处理跨页写入的拆分逻辑否则写入就会错乱。镜像格式方面C6678的RBL对SPI Boot镜像有Magic Word要求TI的文档SPRUGY5里有完整描述。实操中最稳的方式是使用hex6x工具加--boot_table生成带Boot Table的镜像RBL会按表逐段加载。你自己手拼镜像有风险尤其容易在段地址和字节序上出错。注意C6678默认小端模式。生成镜像时确保端序一致否则RBL解析Magic Word、段头时全乱套。另外SPI Flash最前面的内容就是Boot Table不要在前面放文件系统头或者其他无关数据RBL从偏移0开始读取一旦开头不是合法头信息启动直接失败。4.4 Flash擦写耗时会让你重新审视设计很多人第一次烧C6678时会产生一个错觉烧写完就万事大吉。实际上Flash擦写有个容易被低估的耗时问题。一片128Mb SPI NOR Flash全片擦除可能需要几十秒甚至数分钟写入1MB数据也要几十秒。如果你的程序只有几十KB开发阶段每次都擦全片显然浪费时间。优化做法是只擦除需要写的扇区保留其他区域。Writer工具通常支持指定起始地址和长度千万别图省事一键擦全片。烧写耗时还跟SPI时钟速率有关。RBL启动时SPI速率通常固定在较低频率几MHz级别但Writer自己运行时可以把SPI时钟调到更高20MHz以上加快烧写速度。前提是Flash芯片支持这个速率且板卡走线质量足够。5. 启动失败时的完整排查链路附我的经验烧写完成后如果启动失败不要盲目反复拔插电源。我建议按照下面的链路系统排查一般十几分钟内能定位。5.1 常见失败现象对照表现象可能原因排查动作上电后DSP完全没反应电流异常小引导模式配置错误、Flash里无有效镜像检查Boot引脚/拨码开关连接仿真器读DEVSTAT电流比正常稍大但程序没跑起来镜像格式错误、入口地址不对检查Boot Table头确认hex6x转换参数通过仿真器看PC停在0x00800000附近循环或异常程序跳转后异常通常是DDR未初始化检查GEL/Bootloader的DDR3初始化是否执行烧写校验失败Flash电压异常、写保护引脚被拉起、SPI速率太快检查Flash WP引脚降SPI时钟重试Flash能读但程序跑到一半卡住中断向量表未重定位、PLL配置和程序预期不符检查入口处代码、中断向量跳转5.2 一步步定位从示波器抓SPI时序到PC指针定位我自己的排查顺序是固定的第一步确认电源、时钟、复位。用示波器看核心电压0.9V/1.0V是否稳定复位信号是否有毛刺。第二步连接仿真器读取DEVSTAT寄存器确认RBL实际看到的引导模式。如果寄存器值和你想要的Boot模式不一致不要去折腾Flash先修Boot引脚配置。第三步用示波器或逻辑分析仪抓SPI Flash的CS、CLK、MOSI/MISO。复位后如果RBL在尝试读FlashCS引脚肯定会有一次拉低动作。连这个都没有说明RBL根本没走到SPI Boot这一步。第四步抓完波形后在SPI Flash的MOSI线上能看到时钟和数据输出但MISO数据线上没有数据返回那很可能Flash本身有问题或者Flash的供电、片选连接错误。第五步如果Flash有正确的数据输出仍然启动失败连接仿真器复位后读PC指针看它停在哪个地址。如果停在0x00800000附近说明RBL已经跳进了你的镜像停在0x00800000却是0xFFFFFFFF之类的值说明镜像内容根本无效跳到了某个DDR地址但DDR没初始化就是DDR配置问题。5.3 几条可以直接抄走的实操心得第一在应用程序的第一条有效代码处加一个GPIO翻转或点亮LED的操作。往Flash写入之前先用仿真器确认这段代码在0x00800000入口处能触发。这样烧写后如果看到LED亮至少证明RBL跳转成功问题在后边的初始化和环境配置如果LED不亮问题在引导和镜像本身。第二开发阶段用一个小型测试程序做烧写验证不要直接烧写完整的大算法。测试程序就做三件事初始化PLL、点亮LED、循环翻转GPIO。等这个程序能通过Flash正常启动再逐步添加外设和算法逻辑。第三做任何烧写操作前先把原Flash内容备份出来。别以为Flash不会坏我在一次写操作中遇到过供电不稳导致Flash分区表被擦掉的情况幸好之前备了份才能快速恢复。备份可以用Flash Writer的读回功能也可以用示波器逻辑分析仪抓数据不过最方便的还是Writer直接读Flash存成文件。第四SPI Flash的写保护和供电别忽略。很多SPI NOR Flash的WP引脚内部弱上拉外部如果悬空没问题但如果板卡上把这个引脚接到了某颗FPGA的IO而FPGA在上电初期输出低电平那Flash会进入写保护状态表现为写入时报错、校验失败。排查这一类问题可以先把WP引脚强制拉高3.3V试一次。第五C6678多核工程烧写时可以先从单核做起。单核程序烧写跑通后再考虑多核镜像的生成和协同启动。原因很简单多核启动涉及核间通知、共享内存初始化调试复杂度成倍增加如果单核的Flash引导链路没打通多核出问题你根本分不清是Flash问题还是核间同步问题。从工具链配合到GEL初始化从镜像生成到Flash写入再到最后的启动验证成功烧写C6678的要点其实就一句话理解RBL的行为边界确保镜像格式和加载地址匹配它的预期。上面这套流程我在好几个项目里反复用过踩过的坑已经尽量写清楚了。真遇到问题按第5节的思路一格格排查基本都能定位。
阅读完成 · 觉得有帮助?