这一篇在干嘛?

做视频缓存、数据缓冲时,经常发现 FPGA 片内的 ERAM 只有几百 Kbit,根本不够装。安路这篇 APUG011 给了一个聪明的办法:把 EG4S 芯片里自带的 64Mb SDRAM 包一层壳,对外提供一个”长得和 ERAM 一模一样”的读写接口。你按普通 RAM 的方式读写,背后它帮你搞定 SDRAM 那些繁琐的初始化、刷新、时序。本文带你把这份参考设计从头到尾读明白。

先认识 ERAM:FPGA 里的”小仓库”

ERAM 是 FPGA 内部的嵌入式存储资源,在安路器件上通常一个大约 500Kbit 上下,不同系列会有差别。按读写口的方式,ERAM 分三种:

  • 单口 RAM:只有一套地址总线,读和写共用一个口;
  • 简单双口 RAM:两个地址总线,一个专门写、一个专门读,读写时钟可以独立;
  • 真双口 RAM:两个地址总线,两个口都能读能写,读写时钟同样可以独立。

这三种形态决定了你平时怎么用片内 RAM。但不管哪种,容量都是硬伤——几幅高清图像就把它塞满了。容量不够时,常规思路是换更大规模的 FPGA,或者外挂 SDRAM/DDR。可外挂存储需要存储器控制器 IP 支持,对于小型器件或没有存储器硬 IP 的器件来说,开发难度、成本、产品面市时间都是挑战。

方案核心:把 SDRAM 包成”虚拟 ERAM”

APUG011 的思路很直接:利用 Anlogic EG4S 器件内部集成的 64Mb SDRAM(SIP 封装,芯片里自带,不用外挂),在它上面包一层控制器,对用户提供一套和 ERAM 操作方式一致的接口。这样用户只管像操作普通 RAM 那样发地址、发数据,完全不用碰 SDRAM 底层的读写调试。文档里把这个扩展出来的大存储简称为虚拟 ERAM

性能指标先看一眼:

  • 读写时钟可支持 200MHz
  • 32bit 位宽下,理论有效带宽 6.4Gbps,持续读或持续写时实际带宽与理论一致;读写有停顿时也能到 6Gbps 以上,利用率 95% 以上
  • 全部参数化实现,支持任意大小容量、不同位宽的 SDRAM 器件。

从框图看,整个方案中间是一个 SDRAM 控制器,向上暴露用户读写通道(写使能/写地址/写数据/掩码,读使能/读地址/读数据),向下驱动 EG4S 内置的 SDRAM。你拿到的就是上层那几个简单的 RAM 式信号。

最重要的一个限制

因为硬件机制的原因,这套方案不支持读写同时进行,读通道和写通道必须独立排斥运行——要么在写,要么在读,不能一边写一边读。

另外,方案提供两种刷新机制:一种是 IP 自刷新,用户不用参与,只需在读写前判断当前是否正在刷新;另一种由用户通过刷新标志自行控制。正常读写时你不需要操心刷新,但操作前如果用了方案自带的刷新机制,请先判断虚拟 ERAM 是否忙——如果忙,等 Sdr_init_ref_vld 信号变低后再做读写。

怎么写:写通道时序

写通道信号很简单:写使能、写地址、写数据,外加掩码信号(做字节级覆盖写)。每次写的长度没有限制,可以持续写入,甚至一次写完整片 SDRAM。唯一要注意:如果连续写入,SDRAM 时钟要工作在 64MHz 以上

从时序图看,拉高 app_wr_en、给出地址和数据即可连续写入,和操作一块普通 RAM 没有区别。

怎么读:读通道时序

读更简单:给出读使能和读地址,然后等。拉高读使能后约 10 个时钟,控制器开始输出有效的数据输出使能(Sdr_rd_en)和有效数据(Sdr_rd_dout)。这 10 拍是 SDRAM 内部取数的固有延迟,你的下游逻辑要按这个延迟来对齐数据。

数据掩码:只想改其中几个字节怎么办

掩码的作用是”这次写操作里,哪些字节要真正写入,哪些保持原样”。如果每个时钟传输的数据宽度是 32bit,掩码就是 app_wr_dm[3:0],对应关系是:

掩码位控制的字节
app_wr_dm[0]app_wr_din[7:0]
app_wr_dm[1]app_wr_din[15:8]
app_wr_dm[2]app_wr_din[23:16]
app_wr_dm[3]app_wr_din[31:24]

想把整个 32bit 全部屏蔽(不写入)时,把 app_wr_dm 设为 4'b1111 即可。

掩码还有个实用场景:当写入长度不是 4 的倍数时(比如写 5 个数据),第 5 个数据正常写入,第 6~8 个位置是凑数的无效数据,这时把这几个数据的 app_wr_dm[3:0] 设成 4'b1111 屏蔽掉,就不会污染存储内容。

跳跃地址读写:地址怎么跳才合法

FPGA 内的 ERAM 支持任意地址跳跃,本方案理论上也支持,但有个前提:数据是以 128bit 为一组(本方案标准位宽 32bit,即 4 个 32bit = 128bit)来组织的。所以:

  • app_wr_addr[1:0] 必须固定为 2'b00
  • 任意更改的是 app_wr_addr[22:2]
  • 简单理解:每 4 个时钟可以任意跳跃 1 次地址,4 个数据内部的地址只能连续累加。

时序图中的 x 代表任意地址,即组与组之间随便跳。

效率怎么拉满:突发与对齐规则

这套方案”高性能”的秘诀就在对齐规则上。每次写入或读出以 4 个数据为单位,每 4 个中第 1 个地址是首地址,第 2~4 个地址按首地址自动累加:

配合前面说的 SDRAM 200MHz、32bit 位宽,就能算出 6.4Gbps 的理论带宽。要想实际带宽不打折,记住两条:

  1. 每次读写的持续长度尽量是 4 的倍数
  2. 长度实在不是 4 的倍数时,用掩码把凑数的无效数据屏蔽掉。

带宽心得

“持续写”或”持续读”时实际带宽等于理论带宽 6.4Gbps;写读交替且有停顿时也有 6Gbps 以上。所以尽量把应用组织成大块连续读写,而不是零散的单拍访问。

刷新:数据不丢的生命线

和所有 SDRAM 一样,数据必须定期刷新,否则会丢失。方案支持两种刷新方式,默认开启自刷新

  • 自刷新(默认):控制器自己对时钟计数,到点自动刷新。用户需要在 global_def.v 里明确当前控制器时钟频率,用于计算周期——例如工作在 150MHz,时钟周期就是 1000000000/150000000。一般 SDRAM 是 64ms 内对所有行全部刷新一次,最终把 SELF_REFRESH_INTERVAL 的计算结果代入代码即可。
  • 手动刷新:把 sdr_init_ref 模块里的 self_refresh_open 参数传 0,然后用户自行控制 app_ref_req 信号(至少拉高 1 个时钟周期)发起刷新,收到 sdr_ref_ack 表示本次刷新有效。

文档还给了刷新的判断原则:

  1. 数据内容在部分 SDRAM 里如 64ms 内不断被重复读写(比如图像数据),可以选用户自刷新场景,用户不用额外做刷新;
  2. 不刷新,数据内容就会丢失——这是硬性要求。

刷新期间别碰存储

使用方案自带的刷新机制时,操作前先看 Sdr_init_ref_vld:它为高说明正在刷新,等它变低再做读写,否则操作会和刷新冲突。

模块接口一览

顶层只提供用户读写通道信号;SDRAM 器件用的是 EG4S 内部自带的 SIP。若要换到其他 Anlogic 器件,把 SDRAM 部分的接口引出到工程顶层、分配 IO 即可。

信号方向功能说明
Sdr_clkin应用方案 SDRAM 工作时钟
Sdr_clk_sftin同频可能不同相位的 SDRAM 时钟
Rstin高有效
用户读写接口
App_wr_enin写使能,高有效
App_wr_addr[20:0]in写地址
App_wr_din[31:0]in写数据
App_wr_dm[3:0]in写入数据掩码
App_rd_enin读使能
App_rd_addr[20:0]in读地址
Sdr_rd_enout有效读数据输出使能
Sdr_rd_dout[31:0]out有效读出数据
控制信号
Sdr_init_doneoutSDRAM 初始化完成
Sdr_init_ref_vldoutSDRAM 刷新有效标志
Sdr_busyoutSDRAM 忙状态(暂不使用)
App_ref_reqin用户自行请求刷新操作
SDRAM 信号
SDR_CLKinSDRAM 时钟
SDR_CKEinSDRAM 的 CKE 时钟使能
SDR_RASinSDRAM 行有效
SDR_CASinSDRAM 片选
SDR_WEinSDRAM 写使能
SDR_BA[1:0]inSDRAM Bank
SDR_ADDR[10:0]inSDRAM 的操作地址
SDR_DM[3:0]inSDRAM 的数据掩码
SDR_DQ[31:0]inoutSDRAM 数据总线

使用顺序记住三步:先等 Sdr_init_done(初始化完成)→ 再判断是否在刷新 → 然后才能任意长度读写

资源占用:便宜得惊人

在 TD 软件版本 TD5.6.2_71036、EG4S-MINI-DEV3 Demo 板上验证的结果:

资源类型数值
最大频率c0219MHz
使用资源LUT447
REG96
PLL1
IO2
BRAMN/A
DSPN/A
SERDESN/A
适用器件Anlogic 全系器件(除 EG4S 外,其余器件需外挂 SDRAM)

只用了 447 个 LUT 和 96 个寄存器,几乎不占资源,这就是”包一层壳”方案的好处。

调试与仿真

参考设计自带调试和仿真支持:

  • 调试:工程里的 app_wrrd 模块会产生递增的写入数据,读出来后再与递增数比对——数据正确则 Check_OK 信号为低电平,错误则为高电平。看这个信号就能快速确认读写链路是否可靠。

  • 仿真:Test bench 支持 Modelsim 仿真,顶层仿真文件提供 25MHz 时钟让系统跑起来,出错会给出错误提示。指定好库后运行 sim.do 文件即可一键仿真。

工程交付的关键信息:提供 Verilog/VHDL 参考设计、Test bench、IP Model,目标平台为 EG4S20-MINI-DEV3。

移植到自己的工程:10 条清单

  1. 上电后先等 SDRAM 初始化完成再开始工作;
  2. 地址跳跃和掩码严格按第二章的规则来(低 2 位补 0、按 128bit 组跳);
  3. 每次读写操作前判断 Sdr_busy 是否忙;
  4. 读写长度可以任意,但尽量是 4 的倍数;
  5. 地址可以任意跳跃,但以 4 为单位——倍数内不支持跳,倍数间可以任意跳;
  6. 读数据时,不同硬件使用不同的频率和相位,可能出现时钟和数据错一拍的情形,根据实际情况在应用中调整;
  7. 方案可支持 Anlogic 其他系列 FPGA;
  8. 默认使用 64Mb、32bit 宽的 SDRAM,更换器件时改 global_def.v 里的全局宏:
`define DATA_WIDTH  32
`define ADDR_WIDTH  21
`define DM_WIDTH    4
`define ROW_WIDTH   11
`define BA_WIDTH    2
`define SDR_CLK_PERIOD 1000000000/150000000
`define SELF_REFRESH_INTERVAL 64000000/`SDR_CLK_PERIOD/2**`ROW_WIDTH
  1. 确认使用哪种刷新方式,保证数据长期没有操作时不发生变化;
  2. SDRAM 地址位宽在 global_def 文件中更改,因此必须把这个文件 include 进目标工程

最佳实践总结

  • 接口顺序:等初始化 → 避开刷新 → 检查忙 → 再读写;
  • 数据组织:以 4 个数据为一组、长度取 4 的倍数、不足用掩码补齐屏蔽;
  • 连续写入时 SDRAM 时钟不低于 64MHz,想跑满带宽就往 200MHz 上靠。