简介:面向DSP技术及应用实习的EMIF外扩存储器设计工程包,以TI TMS320VC55xx系列数字信号处理器为载体,针对大规模数据处理场景下的外部存储器扩展需求,完整演示了通过外部存储器接口EMIF连接SDRAM等存储设备的设计过程,适合电子工程专业学生、嵌入式开发初学者及参加DSP课程实习的读者。资源共95个文件,压缩包大小约2.1MB,按硬件设计与软件设计分模块组织:硬件部分包括原理图、PCB工程、元件库及封装库,软件部分提供C语言源码、头文件、链接命令文件和编译中间文件,另附readme与设计文档帮助快速上手。目前已有274人学习下载。该工程覆盖了EMIF外扩存储器的核心环节,包括引脚配置、读写时序参数设定、存储器初始化与内存映射,并给出SDRAM、UART等外设的参考实现;通过对照设计文件与代码,可实际验证读写操作,深入理解DSP硬件接口开发方法,也能为后续存储扩展项目提供可直接参考的工程范例。 处理器的外部存储器接口(EMIF)外扩存储器设计,是DSP开发里绕不开的一环,尤其是当你的算法不再只是点个灯、跑个流水线,而是真正要处理数据的时候。前阵子带DSP技术及应用实习,题目就是基于TMS320C6748的EMIF外扩存储器设计,从原理图到PCB再到寄存器配置,完整走了一遍,里面有不少坑值得记录下来。
这篇博客会把方案的选型逻辑讲清楚,把EMIF的地址映射、SDRAM时序换算、硬件电路设计、软件初始化和调试过程中遇到的问题挨个说透。如果你正准备接C6748或者类似的C6000平台板子,想自己外挂SDRAM和Flash,这篇文章应该能帮你省掉大量翻手册和试错的时间。
先说一下实习的要求:用EMIF接口给C6748扩展一片SDRAM和一片NOR Flash,实现上电后从Flash启动,程序能稳定运行,同时要验证大块数据的读写。听起来不复杂,但实际上涉及芯片选型、硬件连线、启动方式、时序配置、软件验证好几层东西,每一层都有值得琢磨的细节。
1. 项目背景与总体设计思路
1.1 为什么C6748需要外扩存储器
C6748这颗芯片内部存储其实不算小:L1P有32KB,L1D有32KB,L2有256KB,总共大约320KB可用。这个容量跑一些控制类程序绰绰有余,但一碰到信号处理类的任务就捉襟见肘了。举个例子,一段64K点的FFT,输入和输出都按单精度浮点算,实部加虚部总共需要64K×4字节×2,等于512KB,L2直接放不下。
这还没说更常见的应用场景。音频设备里做回声消除(AEC)要缓存几百毫秒的参考信号,图像采集需要帧缓冲,哪怕是简单的电机编码器数据采集,想连续记录一段波形做分析,也得有地方放数据。DSP擅长的是计算,不是存储,所以外扩存储器几乎是必然的选择。换句话说,EMIF外扩存储器设计从来不是为了"显得专业",而是实际算法需求的硬性要求。
1.2 外扩方案怎么选:SDRAM加NOR Flash的取舍
确定要外扩之后,第一步是选存储介质。常见候选有SRAM、SDRAM、NOR Flash、NAND Flash这四种,各有各的脾气。
SRAM的优点是不需要刷新,时序控制最简单,但容量做不大,常见也就512KB到4MB,而且单位容量价格最贵,引脚数量也多。SDRAM正好相反,容量从16MB到64MB很常见,成本低,但需要周期性刷新,时序复杂度高。不过好在EMIF控制器把刷新逻辑都接管了,对开发者来说反而没想象中麻烦。NOR Flash适合存程序,可以支持片上执行(XIP),读速度快,但写入慢、容量小。NAND Flash容量大、价格低,但按块读写,还要做坏块管理,对启动加载来说不太友好。
综合考虑,实习方案最终定为SDRAM加NOR Flash组合:SDRAM选IS42S16400J,4M×16bit,共8MB,挂在CS0上,用作程序和数据的主战场;NOR Flash选SST39VF1601,2MB容量,挂在CS2上,用来固化程序。为什么不用DDR2?C6748虽然自带DDR2控制器,但DDR2对布线阻抗、叠层、终端匹配的要求明显更高,实习周期内容易在PCB环节翻车。SDRAM的EM_CLK跑100MHz左右,布线要求相对宽松,作为教学和工程验证完全够用。
2. EMIF接口机制与存储映射设计
2.1 片选空间与地址分配
C6748的EMIFA一共有6个片选空间,分别是CS0、CS2、CS3、CS4、CS5(CS1保留未用)。每个空间的基地址和大小是固定的,比如CS0映射在0x40000000,空间高达512MB,是专门为SDRAM准备的;CS2到CS5则从0x60000000开始,每个空间32MB,适合接NOR Flash、SRAM、NAND这类异步器件。
实际接线时,SDRAM的片选信号CS连到EMIF的CS0,NOR Flash的片选连到CS2。地址线和数据线直接对应连接就行,C6748是16位数据总线,SDRAM也是16位,正好匹配。字节使能信号要特别注意:C6748的BE0对应D[15:8],BE1对应D[7:0],SDRAM的UDQM和LDQM分别接到这两个信号上,这样才能正确支持8位和16位混合访问。
地址映射这块有个容易忽略的点:C6748的EMIF地址空间是按字节寻址的,但SDRAM本身是按16位字组织。设计时先定好基地址,再确定地址线是怎么连接的,避免后面软件访问时地址对不上。实测中很多读写异常都是地址线接反或地址偏移算错导致的,画原理图时反复核对几遍不吃亏。
2.2 SDRAM时序参数换算:从数据手册到寄存器
SDRAM能不能稳定跑起来,关键在于时序参数配置。以IS42S16400J为例,数据手册里能查到这些关键参数:CAS Latency=3时最高时钟133MHz,tRCD(行地址到列地址延迟)典型20ns,tRP(预充电时间)20ns,tRC(行周期时间)66ns,tRAS(行激活时间)45ns,tWR(写恢复时间)14ns。此外还有刷新要求:8192行必须在64ms内全部刷新一遍,平均刷新间隔是7.8125us。
这些参数需要换算成EMIF时序寄存器里的"拍数"。我这次把EM_CLK配置为100MHz,时钟周期10ns,换算就是:tRCD等于20除以10等于2拍,tRP同样2拍,tRC向上取整7拍,tRAS向上取整5拍,tWR向上取整2拍。刷新周期寄存器的值用7.8125us除以10ns,得到781。这一段是整个配置里最核心的计算,每一拍都要留够余量,宁可多一拍也别卡着极限值。
对应的初始化代码结构大致是这样的:
EMIFAConfig emifaCfg; emifaCfg.sdramTiming.tRcd = 2; // tRCD 20ns @100MHz emifaCfg.sdramTiming.tRp = 2; // tRP 20ns emifaCfg.sdramTiming.tRc = 7; // tRC 66ns emifaCfg.sdramTiming.tRas = 5; // tRAS 45ns emifaCfg.sdramTiming.tWr = 2; // tWR 14ns emifaCfg.sdramRefreshPeriod = 781; // 7.8125us / 10ns EMIFAConfigure(&emifaCfg);这里用的是TI StarterWare里的EMIFA驱动接口,字段含义和寄存器一一对应。配置完时序还不够,SDRAM上电后需要执行初始化序列:先发NOP命令,然后预充电、连续刷新、设置模式寄存器,这些步骤EMIF控制器在软件使能后会自动处理。你只需要在初始化时按正确顺序打开各个开关,不能跳步。
3. 硬件电路设计与PCB实操
3.1 存储器电路、启动配置与下载电路
SDRAM电路设计表面看就是"连上就能用",其实有不少细节。每个电源引脚旁边都要放0.1uF去耦电容,而且要尽量靠近芯片引脚。CKE和CLK信号不能悬空,DSP的EM_CLK输出直接给SDRAM做时钟,时钟线建议串一颗22欧姆的电阻,靠近DSP输出端放置,能有效抑制反射。为了让回流路径最短,SDRAM最好和DSP放在PCB的同一面,不要隔层远距离走线。
NOR Flash这边相对简单,SST39VF1601是异步接口,OE、WE、CE分别接到EMIF的对应信号。需要注意Flash的VCC也要做好去耦,特别是烧写瞬间电流变化大,电源纹波可能会导致烧写失败。
启动配置是另一个重点。C6748的启动模式由一组BOOT引脚决定,上电时芯片会锁存这些引脚的电平,然后决定是从NOR Flash启动、NAND启动、SPI启动,还是不启动(No Boot)。很多板子固化程序后无法脱机运行,第一步就要检查BOOT引脚的上下拉电阻是否焊对,是不是误配成了No Boot模式。我在调试的时候习惯先用万用表量一遍引脚电平,确认无误再继续排查软件。
JTAG下载电路这块,C6748用标准14针JTAG接口就够了,TCK、TMS、TDI、TDO四根主要信号加上EMU0、EMU1。特别提醒一下,EMU0和EMU1必须接上拉电阻,一般4.7k到10k都行,否则仿真器可能无法识别芯片。TCK建议加一个下拉电阻,防止悬空时干扰。如果遇到仿真器连接不稳定,可以先查这些上下拉电阻,多半是它们的问题。
3.2 PCB布局布线经验:SDRAM不在快,在稳
SDRAM虽然频率只有100MHz,但对于刚接触高速布线的同学来说,还是不能太随意。数据线组内等长尽量控制在50mil以内,地址线和控制线相对时钟线也要控制长度差,不要让时钟到了、数据还没到。数据线、地址线、控制线最好不要跨分割地平面,否则回流路径被切断,信号质量会明显变差。
时钟线走线要特别小心,尽量短且直,不要打过孔,两侧包地处理,必要时串阻调一下信号沿。SDRAM的正下方区域尽量不要走其他高速信号,保持一个完整的地平面比什么都重要。Flash的布线要求就宽松多了,它是异步器件,对时序不敏感,只要地址数据线别太离谱就行。
我当时犯过一个低级错误:SDRAM的数据线在DSP端和数据端接反了,结果软件读写测试时高字节和低字节对调,排查了很久才用示波器发现是硬件接错。所以画完原理图之后,一定逐根核对一遍总线连接,这一步省不了。
4. 软件配置与读写验证
4.1 EMIF初始化的完整流程
软件初始化的第一步,是确保PLL和时钟树配置正确,让EMIF模块拿到稳定的EM_CLK。很多板子为什么SDRAM初始化失败?先别怀疑寄存器配错,查一下EMIF时钟到底有没有起振。用CCS的寄存器窗口看EMIFA_CFG的值,确认模块时钟使能位已经打开。
接下来按顺序做:先配置SDRAM时序参数,也就是上一章讲的那组tRc、tRcd、tRp、tRas、tWr;再配置刷新周期;最后使能SDRAM控制器。使能之后不要立刻大规模读写,先给SDRAM几十微秒的稳定时间。完整的初始化流程可以这样写:
void EMIF_SDRAM_Init(void) { // 1. 使能EMIF模块时钟 EMIFAModuleClkConfig(); // 2. 配置SDRAM时序与刷新 EMIFAConfig emifaCfg; emifaCfg.sdramTiming.tRcd = 2; emifaCfg.sdramTiming.tRp = 2; emifaCfg.sdramTiming.tRc = 7; emifaCfg.sdramTiming.tRas = 5; emifaCfg.sdramTiming.tWr = 2; emifaCfg.sdramRefreshPeriod = 781; EMIFAConfigure(&emifaCfg); // 3. 使能SDRAM控制器 EMIFASDRAMEnable(); }这里多说一句:在CCS里调试时,也可以写一个GEL文件,在连接目标板的时候自动初始化EMIF。GEL的好处是,你load程序之前SDRAM就已经能访问了,方便早期调试硬件。但最终交付的代码里,还是要老老实实把初始化逻辑放在程序开头,毕竟脱机运行的时候没有CCS帮你初始化。
4.2 读写验证与FFT应用实测
初始化完成后,第一件事是验证基本读写。我的测试方法很简单:向0x40000000起始的SDRAM区域写满0xA5A5A5A5,再读回比较;然后换成0x5A5A5A5A,再把地址值本身写进去读出对比。这三种pattern基本能覆盖数据线短路、地址线错位、字节使能异常等常见问题。测试范围要覆盖整个SDRAM空间,比如从0x40000000到0x40800000遍历8MB。
这类测试通过后,再跑一个有实际意义的应用。我配合定时器配置,让定时器触发ADC采样,采样结果通过EDMA直接搬运到SDRAM缓存区,攒够一整段数据之后调用TI DSPLIB里的FFT函数做分析。外扩SDRAM带来的好处非常直接:之前只能在内部RAM里做4K点FFT,现在可以做64K点甚至更大,频率分辨率提升了16倍,测量相位也稳定得多。可以说,没有足够的外部存储,很多信号处理算法根本没有施展的空间。
Flash烧写和脱机启动验证也不能漏。在CCS里加载程序到SDRAM运行,确认功能正常后,用AIS工具把out文件转成启动镜像,烧写到NOR Flash的起始地址。然后断开仿真器,把开发板重新上电,观察程序是否能从Flash加载并正常运行。这一步是检验整个外扩存储器设计的最终标准。
5. 调试实录:常见问题与排查技巧
5.1 固化后必须接JTAG才能启动,是为什么
这个问题在我带的实习里被问了不下五次:程序在线调试明明一切正常,固化到Flash之后,一拔掉仿真器重新上电,板子就没反应,再连上JTAG又能跑了。看起来像是"需要JTAG才能启动",其实和JTAG本身没任何关系。
根本原因几乎都是BOOT引脚配置不对。C6748的RBL(ROM Boot Loader)在上电时会根据BOOT引脚的电平决定启动方式,如果你把这些引脚配置成了No Boot模式,芯片上电后不会去访问Flash,自然是"一片死寂"。而连上JTAG时,仿真器会通过调试接口直接把程序加载到内存里运行,给人的错觉就是"必须接JTAG才能启动"。排查方法很简单:查BOOT引脚的电平设置表,确认对应的启动模式,用万用表量引脚电平,看是不是硬件上下拉焊得不对。
另一个常见原因是烧写的镜像格式不对。RBL只认特定格式的启动镜像(比如带AIS头的.bin文件),如果你直接把裸的.out文件烧进去,RBL找不到有效的启动头,也会卡死。还有个别情况是Flash型号不被RBL支持,那就要考虑二级引导加载器,先用一小段启动代码初始化SDRAM,再从其他介质加载主程序。
我把这类问题的排查顺序整理成了表格,方便对照:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 掉电重启后完全无反应 | BOOT引脚配置成No Boot | 量BOOT引脚电平,与手册对照 |
| 程序能烧写但启动后跑飞 | 镜像格式错误或地址不对 | 用AIS工具重新生成启动镜像 |
| 偶发能启动,多数时候不行 | Flash型号不被RBL支持 | 更换兼容型号或做二级引导 |
| 连接JTAG才能运行 | 在线调试掩盖了启动问题 | 确认脱机启动模式和镜像文件 |
5.2 数据读写偶发错误与信号质量调试
另一种典型问题是SDRAM大范围读写的时候出现随机错误,不是每次都有,可能跑很久才蹦出一个错误。这种问题排查起来最耗时间。优先怀疑刷新周期配置,这是SDRAM特有的机制,刷新间隔太长数据会丢,太短又占用带宽。用公式算出来的781,实际工作中可以稍微放宽一点,但不能离谱。
然后是时序余量。SDRAM数据手册给的都是典型值或者下限值,实际跑的时候要留足余量。比如tRCD是20ns,在100MHz下等于2拍,如果你把EM_CLK提到133MHz,20ns对应2.66拍,无论如何都塞不下,这时候就必须把EM_CLK降回100MHz,或者换更快的SDRAM。我调试时习惯先把时序参数全部放宽一档,确认功能正常后再逐步收紧找极限,这样能快速判断是功能性问题还是时序余量问题。
电源纹波和布线信号质量也可能导致随机错误。SDRAM读写瞬间电流变化很大,如果电源输入端的滤波电容不够,纹波会直接影响数据采样。用示波器看SDRAM电源引脚的纹波,如果超过100mV,就要加大容量滤波电容或者增加磁珠隔离。信号质量方面,主要看EM_CLK上升沿是否干净,地址数据线有没有明显过冲,这两点都能通过示波器直接观察。
这次实习做完,最直接的体会是:EMIF外扩存储器设计这件事本身不难,但细节多到需要耐心。任何一个时序参数差一拍、一根地址线接反、一个启动引脚电平不对,都会让整个板子表现得像"坏了一样"。后来我把这套SDRAM加Flash的方案直接复用到音频采集项目里,配合定时器触发采样和DSPLIB的FFT做相位测量,跑起来很稳。如果你也在调类似的板子,建议先别急着改PCB,把启动配置和时序参数逐个确认一遍,很多时候问题就出在最容易被忽略的引脚和数字上。
本文还有配套的精品资源,点击获取