YAOTU INSIGHTS

DSP28335 XINTF外扩SRAM完整指南:从时序配置到CMD段分配

DSP28335 XINTF外扩SRAM完整指南:从时序配置到CMD段分配
做DSP开发的人迟早会撞上内存不够用的墙。28335虽然带浮点单元可内部那块SARAM总共也就三十几K字跑个FFT、存一组采集数据、塞个查找表说满就满。这时候最顺手的解法就是走XINTF外扩SRAM。这个实验在普中DSP28335开发攻略里被放在第28章看起来只是“写数据、读数据、校验”但背后牵扯到总线时序、存储映射、CMD文件分段这些真正决定项目能不能跑稳的东西。今天我就按这块板子的实际配置把XINTF外扩SRAM从原理到验证完整捋一遍适合正在照着实验做但没吃透原理的读者也适合想把XINTF用在自己项目里的人。1. 为什么非要用XINTF外扩SRAM28335的内存账和接口布局1.1 内部RAM到底有多大哪些场景会不够先算算28335的家底。芯片内部SARAM分为M0、M1和L0到L7几块M0、M1各1K×16位L0到L7加在一起约32K×16位总共可用RAM大约34K字换算成字节就是68KB左右。日常写个控制循环、PID、简单的状态机这点空间绰绰有余可一旦涉及数据密集型应用立刻露馅。我实际碰到过的场景有三个。第一个是傅里叶变换做256点FFT时输入序列、输出序列、旋转因子表、中间缓冲叠在一起内部RAM就开始紧张更不要说做4096点。第二个是多通道ADC连续采集我需要缓存几百个点的电压波形再丢给上位机处理一开双缓冲内存直接翻倍。第三个是查表类算法比如电机控制里的SVPWM在线计算为了跑实时性把几组曲线预先算好放在RAM里启动时直接查表这时候RAM占用轻松超过内部容量。很多人遇到内存不够第一反应是省着用把数组缩小、把缓冲区复用。这能缓解一部分问题但一旦算法规模上来终究要走到外部扩展这一步。XINTF外扩SRAM的意义就在这里它不是把程序变大而是给运行时数据一个更大的临时仓库让大数组和双缓冲不再需要抠抠搜搜。为什么不直接扩Flash因为Flash用来放代码和常量运行时的变量、栈、堆必须放在可读写的RAM里。外部Flash虽然也能存数据但写入寿命和速度都远不如SRAM所以在需要频繁读写的场景外扩SRAM是更合理的储备方案。1.2 XINTF的外设定位六个Zone、片选与地址映射XINTF全称External Interface是28335片上的异步外部总线接口类似ARM平台上的存储器控制器。它负责把CPU发出的访问请求转换成外部存储器需要的片选、读写、地址和数据信号软件上只需要访问指定的地址硬件会自动完成握手。28335对外部空间按Zone分区管理每个Zone由独立的片选信号XZCS0n、XZCS1n、XZCS2n、XZCS6n、XZCS7n控制。没用到的Zone不会产生片选因此不会误动作。常用映射如下Zone起始地址典型容量片选信号常见用途Zone 00x00004000起独立分配XZCS0n低速外设接口Zone 10x00008000起独立分配XZCS1n低速外设接口Zone 20x0000C000起独立分配XZCS2n外设接口Zone 60x100000起最大512K×16位XZCS6n外扩SRAMZone 70x200000起最大128K×16位XZCS7n外扩Flash做外扩SRAM实验时几乎所有人都选Zone 6因为它的地址空间够大片选对应关系清晰而且和内部RAM、Boot ROM的地址没有冲突。访问Zone 6范围内的任意地址时28335会自动拉低XZCS6n选中接在总线上的SRAM芯片不需要软件额外操作GPIO去控制片选这也是XINTF比普通IO模拟总线省心的根本原因。还有一个容易忽略的点XINTF引脚和很多GPIO是复用的。跑实验前要在GPIO功能选择寄存器里把对应引脚切到XINTF功能否则总线信号根本出不来。这一步通常由初始化代码完成但如果你是照着旧工程改要特别确认引脚复用配置没被别的驱动覆盖。2. 硬件先行的片外SRAM实验从芯片手册到电路连线2.1 典型外扩SRAM的选型与引脚行为这块开发板上常见的SRAM型号是IS61LV25616容量256K×16位也就是4Mb3.3V供电访问时间典型值在12ns左右。引脚结构很简单A0到A17共18根地址线I/O0到I/O15共16根数据线加上CE片选、OE读使能、WE写使能、UB高字节使能、LB低字节使能以及电源和地。理解这颗芯片的关键是UB和LB。IS61LV25616本质上按字节组织UB控制高字节I/O8到I/O15的读写LB控制低字节I/O0到I/O7的读写。当UB和LB同时为低时芯片按16位整字访问一次读写同时操作两个字节只拉低其中一个就变成8位访问模式。这种设计让一颗芯片既能当16位存储器用也能拆成两个8位存储器用非常灵活。在28335外扩SRAM实验中我们的目标就是让CPU直接访问16位数据所以UB和LB在硬件上直接接低电平让它们始终有效。选这颗芯片还有一个实际好处它和TI官方文档里的外扩时序参数能对得上照着数据手册的Read Cycle、Write Cycle时序图配XINTF时序非常顺手。如果你手上的板子换成了其他SRAM比如容量更大的IS61LV51216引脚排布基本类似只是A18、A19多出两根地址线接线原则不变。真正需要重新核对的是访问时间参数这会影响后面的时序配置后面会细说。2.2 28335与SRAM的接线逻辑和字寻址细节拿典型板载接线来说28335的XINTF信号和IS61LV25616的对应关系如下28335引脚SRAM引脚说明XA0 ~ XA17A0 ~ A17地址总线XD0 ~ XD15I/O0 ~ I/O15数据总线XZCS6nCEZone 6片选XRDnOE读使能XWE0nWE写使能低电平UB、LB16位整字访问这个连线很容易看懂但有一个底层概念必须搞清楚28335的地址是16位字地址不是字节地址。C28x系列CPU的最小寻址单位是16位字所以外部地址总线XA0对应的就是“第几个16位字”而不是像ARM那样对应字节。外部SRAM的A0地址线也是按16位字组织两者天然对齐所以不需要做任何地址移位。举个具体例子。CPU访问地址0x100000时XA0到XA17输出全0选中SRAM的第0个字。访问0x100001时XA0变1选中第1个字。换句话说28335地址加1对应SRAM地址加1访问的就是下一个16位数据。你在代码里写一个Uint16数组指针指针加1跳过一个Uint16正好和硬件行为完全一致。很多从ARM转过来的人一开始容易懵因为ARM的地址最小单位是字节访问16位数据时地址要偶数递增DSP却不需要。想通这一点写地址循环的时候就不会出现“隔一个地址跳一个数据”之类的低级错误。2.3 关于XA/XA和字节使能的一个常见误区调试时有两类问题算得上经典误区。第一类是有人看到28335有XWE0n和XWE1n两个写使能引脚第一反应是“这是不是要分别控制高字节和低字节”于是把XWE0n接UB、XWE1n接LB结果怎么写都不对。实际上28335在16位外部数据总线模式下访问16位数据时产生的写脉冲同时体现在XWE0n和XWE1n上两个信号不需要在SRAM端分开接。板子原理图通常把XWE0n接到SRAM的WEUB、LB直接拉低这样CPU每次写操作都会整字写入简单可靠。不要画蛇添足去处理字节使能。第二类是地址线高位和片选的混淆。有人会把XA18、XA19也接到SRAM上想“多扩展一点空间”却忘了28335的Zone 6片选XZCS6n只在0x100000到0x17FFFF范围内有效。如果SRAM容量超过Zone 6剩余空间或者地址线超过了芯片实际内部地址位数高地址位就会悬空访问地址和物理存储单元的对应关系错乱。正确做法是SRAM有多少根地址线就接多少根多出来的地址位不要乱接片选信号只走XZCS6n。总结一句话硬件连线以“16位数据总线、Zone 6片选、UB和LB接地”为基准不要自己发挥。只要线和地址对得上后面的软件时序配置才有意义。3. 配置XINTF的完整思路分频、时序与使能序列3.1 先做时钟分频XTIMCLK怎么选XINTF本身有一套独立的时钟逻辑时钟源是系统时钟SYSCLKOUT也就是CPU主频。28335最高能跑到150MHz但在配置XINTF时可以先决定把XINTF时钟设为SYSCLKOUT还是SYSCLKOUT的一半对应的是XINTFCNF2寄存器里的XTIMCLK位。为什么要纠结这个分频因为外部SRAM的访问速度是固定的IS61LV25616的访问时间典型12ns而150MHz下XINTF时钟周期只有6.67ns。时序参数一个周期对应6.67ns想凑出足够的访问窗口active阶段至少得给2个周期加上lead和trail整次访问的周期数会比较多。如果把XTIMCLK设为1XINTF时钟降到75MHz周期变成13.3ns同样一个周期能提供更长的时间窗口时序余量会宽裕很多。我自己的经验是外扩SRAM实验阶段优先选择XTIMCLK等于SYSCLKOUT的一半先把功能跑通再回头优化速度。追求性能时再尝试全速模式但此时必须严格检查SRAM的建立时间、保持时间是否满足。还有一点要提醒28335的SYSCLKOUT取决于PLL配置。老例程里PLL可能只配到100MHz甚至更低新工程可能直接150MHz。同样写一个active等于7实际等待时间完全不同。所以看时序参数之前先确认SYSCLKOUT到底是多少否则算出来的时间全是空中楼阁。另外XINTF外设时钟使能位PCLKCR0里的XINTFENCLK也要提前打开。这个位控制XINTF模块时钟不打开的话后面无论怎么配置XTIMING总线都不会响应外部访问。3.2 lead/active/trail三段时序怎么和SRAM手册对账XINTF把一次外部访问拆成三段lead是片选有效后到读写信号有效前的准备时间active是读写信号有效的核心窗口trail是读写信号结束后到片选撤销前的保持时间。三段都和时钟周期数相关配置寄存器里按周期数写所以真正要算的是“几个周期够用”。典型配置代码如下按照Zone 6对应的XTIMING6寄存器设置XintfRegs.XTIMING6.bit.XWIDTH 0; // 0表示16位数据总线 XintfRegs.XTIMING6.bit.XRDLEAD 3; // 读建立3个XINTF时钟周期 XintfRegs.XTIMING6.bit.XRDACTIVE 7; // 读有效7个周期 XintfRegs.XTIMING6.bit.XRDTRAIL 3; // 读保持3个周期 XintfRegs.XTIMING6.bit.XWRLEAD 3; // 写建立3个周期 XintfRegs.XTIMING6.bit.XWRACTIVE 7; // 写有效7个周期 XintfRegs.XTIMING6.bit.XWRTRAIL 3; // 写保持3个周期这个配置不是拍脑袋写的。当XTIMCLK设为SYSCLKOUT/2且SYSCLKOUT为150MHz时XINTF时钟75MHz周期约13.3ns。active阶段7个周期就是93.1ns远大于IS61LV25616的12ns典型访问时间所以读操作肯定能在有效窗口内稳定返回数据。整体一次访问大约(373)*13.3173ns虽然不算快但对于SRAM验证实验完全足够。计算总访问时间有个简单公式总访问时间 (LEAD ACTIVE TRAIL) * T_XINTFCLK 读取数据有效时间 ACTIVE * T_XINTFCLK配置时用SRAM手册里的tAA地址访问时间、tRC读周期时间、tWC写周期时间做对账。SRAM手册给出的是最小时间要求你要保证算出来的active阶段时间不小于tAA整段lead加active加trail不小于tRC或tWC。如果实际跑起来偶发错误优先加大active再考虑加大lead和trail。另外要注意XWIDTH字段。28335外部总线支持16位和32位两种宽度用IS61LV25616这种16位芯片时XWIDTH必须设0。有些例程会把XWIDTH设成1那是对应外部接了32位存储器的板子套到16位SRAM上会直接读写错乱。3.3 别忘了那几行“神秘”的使能汇编配置完XTIMING很多人觉得就完事了结果一运行访问外部RAM要么卡死要么读回来全是0xFF。问题往往出在一个容易被忽略的环节XINTF接口在配置变化后需要执行一次特殊的总线操作序列才能真正使能。TI官方例程里InitXintf函数的最后通常有一段看起来像“随便写了几个数”的代码实际上是通过连续几次对外部接口的访问让XINTF内部状态机完成初始化。这步操作的原理比较复杂简单说就是XINTF的时序参数虽然已经写进寄存器但总线状态机需要一次真实访问来同步和启动。很多从旧工程复制代码的人只复制了XTIMING配置部分漏掉了后面的使能序列于是XINTF完全不动。标准做法是直接从CCS安装目录下的DSP2833x_Xintf.c文件里找到InitXintf函数保留完整的初始化逻辑不要自己删减。如果你用的是普中开发板配套例程XINTF初始化函数一般已经封装好调用后不要在后面再重复写XTIMING去覆盖它的配置。这里区分两个概念前面说的PCLKCR0.XINTFENCLK是给模块供时钟后面这段访问序列是激活外部总线工作状态两者缺一不可。只开时钟不激活总线外部RAM就是一块“电压正常的死芯片”。4. 把变量放进外部SRAMCMD文件与工程配合4.1 在CMD里给外部RAM画一块区域XINTF配置好了硬件上CPU访问0x100000地址就能读写外部SRAM。但如果你想在C语言里直接用数组名访问外部RAM光有硬件还不够必须让链接器知道有一块外部RAM区域可以用来放段。这就要改CMD文件。CMD是链接命令文件负责告诉链接器芯片的存储布局以及代码数据段放在哪里。以内扩SRAM实验为例在CMD的MEMORY里新增一块区域MEMORY { PAGE 1: ZONE6SRAM : origin 0x100000, length 0x40000 }length填0x40000也就是256K字正好匹配IS61LV25616的容量。如果你的SRAM是512K字就把length改成0x80000。随后把自定义段和这块区域绑定SECTION { .ext_ram : ZONE6SRAM, PAGE 1 }这样凡是放进.ext_ram段的变量链接器会自动分配到外部RAM地址空间。需要注意PAGE的选择。外部RAM既可以放PAGE 1的变量数据也可以放PAGE 0的只读常量具体看用途。常见的做法是把可读写变量放PAGE 1符合大家平时使用RAM的习惯。改CMD文件有个必须守住的底线外部RAM区域不能和内部RAM、外设寄存器空间重叠。如果origin和length算出来覆盖到别的有效区域链接器不一定会报错但程序运行起来数据会被莫名覆盖排查起来非常痛苦。4.2 直接地址访问和大数组访问的两种验证方式验证外扩SRAM有两种常见写法各有适用场景。第一种是直接地址访问定义指针指向0x100000然后循环读写volatile Uint16 *extRam (volatile Uint16 *)0x100000; Uint16 i; Uint16 error 0; for (i 0; i 0x1000; i) { extRam[i] i; } for (i 0; i 0x1000; i) { if (extRam[i] ! i) { error 1; break; } }这种方式的优点是直观不依赖CMD文件哪怕链接脚本还没改也能立刻验证XINTF总线通不通。实验初期建议先用它跑一遍把问题限定在硬件和XINTF配置层面。第二种是数组加段绑定用#pragma把数组放到自定义段里#pragma DATA_SECTION(extBuf, .ext_ram) Uint16 extBuf[0x1000];这样写的好处是代码更贴近真实工程后续要搬大块数据到外部RAM时直接声明数组就能用。劣势是如果CMD文件没改对链接器可能把数组放回内部RAM而不报错造成“以为在测外部RAM、实际测的是内部RAM”的假象。判断方法就是打开编译生成的.map文件搜extBuf的地址确认落在0x100000到0x13FFFF范围内。两种方式建议结合使用。先指针验证硬件再数组验证工程配置哪一个环节出错都能快速定位。全量测试时注意边界比如从0x100000写到0x13FFFF再回来读特别检查最后一个地址很多数据线问题都藏在最高地址位翻转附近。5. 实测过程中的异常现象与排查经验5.1 读回全FF先查外设时钟和片选映射最经典的现象是程序跑完读回来的数据全是0xFF写进去的数据像没写一样。遇到这种情况不要急着怀疑SRAM芯片坏了按顺序查三个地方。第一查外设时钟看PCLKCR0里的XINTFENCLK是否为1。很多从旧工程复制的初始化函数里没有打开这个位XINTF模块根本不在工作状态。第二查引脚复用打开GPIO功能选择寄存器确认XA、XD、XZCS6n、XRDn、XWE0n这些引脚都配置成了XINTF功能而不是普通GPIO。有些板载LED驱动或者按键扫描代码会把邻近引脚重新配成GPIO覆盖掉XINTF配置。第三查访问地址确认代码里polling的地址范围落在Zone 6的0x100000到0x13FFFF或0x17FFFF之间。如果地址落在未映射区域片选不会拉低SRAM自然不响应。排查顺序很重要先查时钟和映射再动硬件。我见过有人一上来就换SRAM芯片结果问题只是没开外设时钟白白折腾了大半天。还有一种伪装成“全FF”的情况代码里加了编译器优化循环写操作被优化掉了。此时读出来的全是上电初值看似全是FF。解决方法是给外部RAM指针加上volatile告诉编译器这些地址有硬件副作用不要随意合并或删除访问。5.2 偶发性写读不一致时序余量不足的典型特征比“全FF”更折磨人的是偶发性错误。现象是跑小范围测试没问题跑全量测试偶尔读回来一个错数据再跑一次又对错误地址看起来没什么规律。这种问题绝大多数是时序余量不足。SRAM的建立时间、保持时间有一个最小要求如果XINTF的lead、active、trail配置得太紧凑芯片在某些电气条件下就会偶发采样失败。温度变化、电源纹波、总线电容都会影响边界所以小范围测试时运气好全过数据量一上来就暴露。处理办法简单粗暴把时序参数整体加大。优先加大active因为这是数据从SRAM到DSP的总线导通窗口再把lead和trail各加1到2个周期。如果XTIMCLK用的是SYSCLKOUT全速可以直接改成半速让每个周期的时间拉长通常立刻见效。偶尔也会有另一种情况错误地址集中在某些bit上比如总是bit11读错。这种通常是地址线或数据线虚焊、排线接触不良属于硬件问题和时序无关。区分方法是把同样的测试重复几次如果错的bit固定不变优先怀疑焊接和连线而不要继续折腾寄存器。5.3 数据放错地的迷惑行为编译段分配陷阱数组方式验证时有人会遇到一个特别迷惑的现象外部RAM明明能通过指针方式正常读写但声明的数组在运行时却不像外部RAM要么数据不持久要么和别的变量冲突。问题几乎都出在段分配上。最常见的是#pragma DATA_SECTION和CMD文件名不一致比如代码里写“.ext_ram”CMD里写“.ext_ram_data”链接器找不到匹配段变量就会按照默认规则放回内部RAM。另一种情况是CMD文件有多个工程链接时只包含了其中一个你改的那个根本没参与链接。排查方法非常直接编译后打开.map文件找到你的数组符号看它落在哪个地址区间。如果地址是0x00xxxx或者0x80xxxx说明它还在内部RAM段绑定没有生效。地图文件是调试这类问题最好的朋友比猜代码效率高得多。还有一种隐蔽情况是数组长度超过CMD定义的范围。比如SRAM只有256K字你在CMD里length写0x40000但数组申请了0x50000个字链接器会报溢出错误。不过有些工程配置不严谨选择了“忽略溢出警告”结果数组尾部写到外部RAM之外的未映射空间运行时间歇性掉数据。所以我建议数组测试先从0x1000这样的小规模开始确认段布局正确后再扩展到目标容量。5.4 用仿真器观察总线的检查顺序如果代码和配置都查过问题还是反复出现就得借助CCS的调试功能。在CCS的Memory Browser窗口里直接输入0x100000能查看外部RAM当前内容写值后立即刷新能判断总线是否正常工作。这个操作比跑全量循环定位问题快得多适合在第一次上电时验证基本通路。另一个实用技巧是在访问外部RAM的代码前后设置断点然后查看相关寄存器的值确认XTIMING6被正确写入以及XINTFCNF2里的分频位设置符合预期。仿真器连接正常的话读寄存器值非常直观几个关键字段一对比就知道配置有没有生效。如果手头有逻辑分析仪或示波器可以抓XZCS6n、XRDn、XWE0n这几个信号观察一轮访问过程中lead、active、trail的时间长度是否和寄存器配置一致。这种方法能直接看到时序波形排查硬件问题时最有说服力。不过对大多数实验来说稳定读写通过就已经够了不必强制上仪器。我第一次做这个实验的时候卡在最简单的写读回上程序跑完读回来全是FF查了半天才发现是PCLKCR0没使能。后来又把时序调得很紧结果小范围对、大范围错最后老老实实把XTIMCLK降到半速才稳下来。折腾完最大的体会是XINTF外扩SRAM的调试不要靠猜先查时钟再看映射然后确认CMD段位置最后才动时序。按这个顺序走大多数问题都能在半小时内解决。