DSP与FPGA的EMIF数据通路设计:从地址映射到时序对接

发布时间:2026/10/29 20:37:51
DSP与FPGA的EMIF数据通路设计:从地址映射到时序对接

简介:面向DSP与FPGA协同开发初学者及嵌入式工程师的接口通信学习资料,聚焦TMS320C6000系列DSP通过EMIF外部存储器接口与FPGA进行数据交互这一典型场景,帮助读者理解可配置总线接口的寄存器配置、读写时序设置与片选地址映射等核心问题。压缩包内为1个docx文档,约597KB,以图文形式梳理EMIF接口概念、存储器映射寄存器、EMIF16初始化参数、.cmd文件地址分配及load memory数据格式等内容,并附有片选CS4发送数据、位宽与大小端对应关系等实例说明。该资料已有8327人学习下载,适合作为硬件联调前的概念梳理与配置参考,读者可从中获取EMIF接口从初始化到与FPGA通信的完整知识框架,以及时序参数设置和地址空间划分的实践思路,便于对照自身硬件连线快速定位片选与数据读写问题。

1. 需求先立住:为什么DSP和FPGA之间要专门修一条EMIF通路

做信号处理类项目,绕不开一个分工问题:DSP擅长确定性强的乘加运算和实时调度,FPGA擅长并行流水和自定义时序接口。两者凑在一起,最直接的需求就是“DSP把一批数据交给FPGA处理,FPGA处理完再交回来”。这条通路怎么修,是方案里第一个要拍板的事。

常见做法有几种:双口RAM、SPI、UART、以及EMIF。前几种要么带宽不够,要么协议开销大,要么需要额外器件。EMIF的优势在于它本来就是DSP用来挂存储器的总线,地址线、数据线、片选、读写使能一应俱全,把它当成一条“可配置的并行总线”去对接FPGA,硬件上几乎不需要额外芯片,软件上就是往某个地址写数、从某个地址读数。这套方案要解决的,就是把这件“看起来只是读写内存”的事,做成一条稳定、可复现、能验收的DSP-FPGA数据通路。适合正在做毕设或课设、需要把DSP和FPGA真正连起来跑通数据的学生,也适合要交付一块信号处理板卡的工程师。

2. 总体方案设计:从地址空间到片选信号的完整链路

2.1 先理清EMIF到底在扮演什么角色

EMIF(External Memory Interface)在DSP上是一个可配置的总线控制器。它对外提供时钟ECLKOUT、片选CE、读写使能、字节使能、地址线和双向数据线。通过配置寄存器,可以决定总线位宽是8位还是16位、工作在异步NOR/ASRAM模式还是NAND模式、读写各阶段的建立/选通/保持周期数。正因为这些时序参数可调,FPGA这种“非标准存储器”才能被当成一个异步SRAM挂上去。

方案里要明确一点:DSP并不“知道”对面是FPGA,它只知道自己往某个地址写了一个数,EMIF负责把地址、数据、片选、写使能按时序摆到管脚上。FPGA侧要做的,是识别这组时序,把数据接住。理解了这个角色分配,后面所有配置和代码才有落脚点。

2.2 地址空间划分与.cmd文件

DSP的地址分配由链接命令文件(.cmd)决定。EMIF的每个片选空间对应一段固定地址,具体哪段地址落到哪个CE,由硬件连线决定。一个典型的映射如下:

MEMORY { LOCAL_L2_SRAM: o = 0x00800000 l = 0x00080000 /* 512kB LOCAL L2/SRAM */ LOCAL_L1P_SRAM: o = 0x00E00000 l = 0x00008000 /* 32kB LOCAL L1P/SRAM */ LOCAL_L1D_SRAM: o = 0x00F00000 l = 0x00008000 /* 32kB LOCAL L1D/SRAM */ SHRAM: o = 0x0C000000 l = 0x00400000 /* 4MB 多核共享存储 */ EMIF16_CS2: o = 0x70000000 l = 0x04000000 /* 64MB EMIF16 CS2 */ EMIF16_CS3: o = 0x74000000 l = 0x04000000 /* 64MB EMIF16 CS3 */ EMIF16_CS4: o = 0x78000000 l = 0x04000000 /* 64MB EMIF16 CS4 */ EMIF16_CS5: o = 0x7C000000 l = 0x04000000 /* 64MB EMIF16 CS5 */ DDR3: o = 0x80000000 l = 0x80000000 /* 2GB 外部DDR3 */ }

这段配置的含义是:把CS4空间划到0x78000000起始的64MB区域。当程序访问这个区间内的地址时,EMIF会自动拉低对应的CE4片选。需要强调的是,片选和地址的对应关系不是软件随便定的,而是硬件原理图上CE引脚接到FPGA的哪个bank决定的。软件能改的是这段空间里放什么、怎么访问,改不了“CS4对应0x78000000”这个事实。做方案时先把原理图上的片选分配确认清楚,再回来写.cmd,顺序不能反。

2.3 数据流向:DDR3做数据池,EMIF做搬运通道

实际项目里,待处理的数据量往往远超DSP片内存储。方案采用的做法是:DSP先把数据从DDR3取出,再通过EMIF写到FPGA;FPGA处理完,DSP再从EMIF读回,写回DDR3。DDR3在这里充当数据池,EMIF是搬运通道。

DDR3需要先初始化才能使用。初始化完成后,通过CCS的Load Memory功能可以把测试数据直接灌进DDR3。Load Memory使用的TI data格式首行是格式说明,例如1651 9 80000000 0 A200 4,其中80000000是首地址,A200是数据个数。这里有一个容易让人卡住的细节:DDR3按32位十六进制存储时,00010203 04050607这样的内容,如果EMIF位宽配成16位,读出来的字节序会变成0203 0001 0607 0405,也就是小端对应低地址。地址80000000对应0203,80000001对应0001。初看会觉得和“07才是80000004的数据”对不上,但这是位宽转换和字节序共同作用的结果,方案里要提前把这个预期写清楚,否则调试时会被数据错位折腾很久。

3. EMIF初始化与FPGA侧时序对接的实现

3.1 EMIF16初始化函数的参数怎么定

EMIF的初始化核心是填一组配置结构体。下面这段是CS2空间按16位NOR/ASRAM模式对接FPGA的典型配置:

/* EMIF16 初始化:CS2 空间对接 FPGA,16位异步NOR/ASRAM模式 */ EMIF16_CFG emif16_cfg; emif16_cfg.maxWait = 0x80; /* 最大等待周期,防止总线挂死 */ emif16_cfg.wait0Polarity = EMIF_LOW_WAIT; /* WAIT0 低有效 */ emif16_cfg.wait1Polarity = EMIF_LOW_WAIT; /* WAIT1 低有效 */ emif16_cfg.ceCfg[0]->busWidth = EMIF_BUS_16BIT; /* 数据线16位 */ emif16_cfg.ceCfg[0]->opMode = NOR_ASRAM_MODE; /* FPGA按异步SRAM对待 */ emif16_cfg.ceCfg[0]->strobeMode = SS_STROBE; /* 片选选通模式 */ emif16_cfg.ceCfg[0]->waitMode = EMIF_WAIT_NONE; /* 本方案不用WAIT握手 */ /* 写时序:建立15拍、选通63拍、保持7拍 */ emif16_cfg.ceCfg[0]->wrSetup = 15; emif16_cfg.ceCfg[0]->wrStrobe = 63; emif16_cfg.ceCfg[0]->wrHold = 7; /* 读时序:与写时序对称配置 */ emif16_cfg.ceCfg[0]->rdSetup = 15; emif16_cfg.ceCfg[0]->rdStrobe = 63; emif16_cfg.ceCfg[0]->rdHold = 7; emif16_cfg.ceCfg[0]->turnAroundCycles = 3; /* 读写切换时的总线周转周期 */ emif16_cfg.ceCfg[0]->nor_pg_Cfg = NULL; /* 非NAND模式,不配页参数 */

参数选择的逻辑是这样的:busWidth必须和FPGA侧数据线宽度一致,16位是最常见的折中;opMode选NOR_ASRAM_MODE,因为FPGA被当成异步SRAM,不需要NAND的命令锁存周期;wrSetup/wrStrobe/wrHold三个值决定了写使能拉低前后各留多少时钟拍,值越大越稳但吞吐越低,初版可以给宽一点,跑通后再压。turnAroundCycles是读转写、写转读之间的空闲拍,防止双向数据线冲突。这些参数的具体含义在EMIF用户手册(如sprugz3a)里有逐位说明,改任何一个都要回去对照时序图。

3.2 DSP侧读写就是一次指针赋值

配置完成后,DSP访问FPGA在代码层面极其简单:

/* 向CS4空间偏移0x2E处写入16位数据0x01 */ *((unsigned short *)(0x7800002E)) = 0x01; /* 从同一地址读回一个16位数据 */ unsigned short rd_val = *((unsigned short *)(0x7800002E));

这两行之所以能工作,是因为EMIF硬件会自动处理片选、地址、读写使能和字节使能的时序,软件只需要给出地址和数据。地址0x7800002E落在CS4空间内,所以CE4会被拉低;unsigned short决定了这是一次16位访问。方案里要提醒的是:指针类型必须和总线位宽匹配,用unsigned short配16位、用unsigned char配8位,类型写错会导致字节使能异常,读回来的数据高位是垃圾。

3.3 FPGA侧如何识别一次有效的读写

FPGA不认识“地址0x7800002E”,它只看到管脚上的电平变化。识别一次写操作,靠的是写使能的下降沿加片选有效:

-- 检测DSP写使能下降沿,且片选为CS4,产生一个周期的写有效脉冲 wstb_o <= '1' when (dsp_emif_wen_t1 = '1' and dsp_emif_wen_t = '0' and emif_ce_n = "01") else '0'; -- 检测DSP读使能下降沿,且片选为CS4,产生读有效脉冲 rstb_o <= '1' when (dsp_emif_oen = '1' and dsp_emif_oen_i = '0' and emif_ce_n = "01") else '0';

dsp_emif_wen_t1和dsp_emif_wen_t是写使能打一拍前后的值,两者组合出下降沿;emif_ce_n判断当前片选是不是目标空间。写有效脉冲出现的那一拍,数据线上的值就是DSP写来的有效数据,FPGA应当在这个脉冲内把数据锁进寄存器。读方向同理,读有效脉冲出现时,FPGA必须已经把要返回的数据摆到数据线上,因为DSP在选通周期内就会采样。

3.4 地址位调整:一个必须写进方案的细节

FPGA侧拿到的地址线并不是直接可用的。方案里要把地址位调整明确写出来:需要把第23位和第22位调整到第0位和第1位。按用户手册的说法,这种调整是8位位宽才需要的,但实际调试中16位位宽同样要做这个调整,否则地址译码对不上。这一条属于“手册和实测不一致”的典型情况,方案里不能只抄手册,要把实测结论标注清楚,并说明调整后的地址译码逻辑要和DSP侧访问的偏移量一一对应验证。

4. 避坑与排查:这条通路上最容易翻车的五个点

4.1 片选对了但数据全错:位宽和指针类型不匹配

现象是写进去一个数,读回来高字节或低字节是乱的。原因通常是EMIF配了16位,但代码里用了8位指针,或者FPGA侧按8位锁存。解决方法是把busWidth、指针类型、FPGA数据锁存宽度三者对齐,改完用固定图案(如0xAAAA、0x5555)逐位验证。

4.2 读回数据滞后一个地址:字节序和地址递增理解偏差

现象是读到的数据像是“前一个地址的内容”。原因在于DDR3的32位存储转16位EMIF时,小端映射让低地址对应的是高半字。解决方法是按方案里写的映射关系(80000000对应0203、80000001对应0001)先手工推一遍预期值,再和实测对比,不要凭直觉判断“07应该在80000004”。

4.3 总线偶尔挂死:时序参数给得太紧

现象是大部分传输正常,偶发某次读写后DSP卡住。原因是wrStrobe或rdStrobe太小,FPGA还没准备好数据DSP就采样了,或者双向数据线切换时没有足够的周转拍。解决方法先把turnAroundCycles和三个时序参数放大,确认稳定后再逐步压缩,每次只改一个参数。

4.4 FPGA收不到写脉冲:边沿检测漏了片选条件

现象是DSP明明执行了写操作,FPGA的写有效信号不动作。原因是边沿检测只看了写使能,没把片选有效纳入条件,或者片选编码写错(比如把"01"写成了"10")。解决方法是用逻辑分析仪同时抓写使能、片选、地址和数据,确认脉冲产生的那一拍四个信号是否同时满足条件。

4.5 读方向数据冲突:FPGA没及时释放数据线

现象是连续读写交替时数据错乱。原因是FPGA在读周期结束后没有把数据线切回高阻,DSP开始写时两边同时驱动。解决方法是在FPGA侧用读有效信号控制数据线输出使能,读脉冲一结束立即置高阻,并在方案里把双向口的inout写法固定下来。

5. 验收与答辩:怎么证明这条通路真的通了

验收这件事,最忌讳只演示“写一个数读一个数”。答辩时评委最容易追问的是:你怎么证明数据是完整、正确、可重复地过去的?我一般会准备三层验证,从弱到强。

第一层是单点读写。固定地址写固定图案,读回比对,覆盖0x0000、0xFFFF、0xAAAA、0x5555四个边界值,证明基本通路和位宽配置正确。第二层是地址遍历。在一段连续地址上写入递增序列,再整段读回比对,证明地址译码和地址位调整没有错位。第三层是块传输加回环。DSP从DDR3取一段真实数据,经EMIF写入FPGA,FPGA做一次可逆处理(比如取反或加固定偏移)后写回,DSP读回并与预期结果逐字节比对。这一层能同时验证数据完整性、时序稳定性和双向切换。

验收记录建议做成一张表,把每层的测试项、预期值、实测值、结论列清楚:

测试层测试内容判定标准
单点读写四个边界图案写入读回完全一致
地址遍历连续256个地址递增序列无错位、无丢失
块传输回环1KB真实数据经FPGA处理后读回逐字节一致

答辩时还有一个技巧:主动把“没解的问题”讲出来。比如方案里地址位调整与手册描述不一致这件事,与其被问出来,不如自己先说清楚“手册说8位才需要,实测16位也要,我按实测做了并验证通过”。这种坦诚反而比全说“没问题”更让人信服。另外,时序参数为什么取这组值,要能说出“先放宽跑通、再逐步压缩、当前值留了多少余量”,而不是背一个数字。

从那以后我每次做DSP和FPGA对接,都强制先把片选映射、位宽、字节序三件事写成一张对照表贴在工位上,跑通第一笔数据之前不写任何业务逻辑。这条习惯帮我省掉了太多返工。希望帮到你。

本文还有配套的精品资源,点击获取