EBR基础:嵌入式块RAM的核心概念

各位同学,今天我们来聊聊Lattice FPGA里一个非常重要的资源——EBR,也就是嵌入式块RAM。说白了,它就是FPGA内部集成的专用存储单元,跟我们平时用的外部SRAM、SDRAM不同,EBR是直接嵌入在芯片内部的。

我个人习惯把EBR理解成「FPGA的随身笔记本」。为什么这么说?因为它容量不大,但胜在速度快、使用灵活,而且就在芯片内部,不需要通过IO引脚去访问外部存储器。我在做视频处理项目时,经常用EBR来做行缓存,效果非常好。

EBR的基本概念

EBR的全称是Embedded Block RAM,每个EBR都是一个独立的存储块。在Lattice的FPGA中,每个EBR的容量是固定的——18Kbit,也就是18,432个比特位。

你可能会问:「18Kbit能存多少数据?」嗯,我们来算一下:

  • 如果配置成8位宽,可以存2,304个字节
  • 如果配置成16位宽,可以存1,152个半字
  • 如果配置成32位宽,可以存576个字

听起来不大对吧?但别忘了,FPGA里可以有几十个甚至上百个EBR。把它们组合起来,容量就很可观了。

关键点:EBR是同步RAM,所有读写操作都在时钟上升沿触发。这一点跟异步RAM完全不同,初学者容易在这里栽跟头。

EBR的容量与组织方式

每个EBR虽然是18Kbit,但实际可用的数据位是16Kbit,另外2Kbit是用于校验的。不过说实话,在大多数设计中,我们直接用16Kbit就够了。

EBR支持多种位宽配置,我整理了一个表格,方便大家查阅:

配置模式 数据位宽 地址深度 实际使用容量
单端口 1~36位 16K~512 16Kbit
双端口 1~18位 16K~1K 16Kbit
真双端口 1~18位 16K~1K 16Kbit

注意看,单端口模式下位宽可以到36位,而双端口和真双端口只能到18位。为什么?因为双端口需要两套地址和数据总线,芯片面积有限,所以位宽减半。这个我在设计DDR控制器时深有体会。

三种配置模式详解

1. 单端口模式

单端口模式是最简单的配置方式。它只有一个端口,读写操作共用一套地址和数据总线。

特点:

  • 同一时刻只能读或写,不能同时进行
  • 控制逻辑最简单,资源消耗最少
  • 适合做FIFO、寄存器堆等场景

我曾经在一个通信项目里用单端口EBR做参数查找表,把几百个配置参数存进去,CPU通过地址直接读取,简单又高效。

小技巧:单端口模式下,如果读地址和写地址相同,读出的数据是旧数据还是新数据?这取决于具体的FPGA型号。Lattice的做法是读出旧数据,这一点跟Xilinx不同。设计时一定要注意。

2. 双端口模式

双端口模式有两个端口:端口A和端口B。每个端口都可以独立读写,但共享同一块存储空间

特点:

  • 两个端口可以同时操作,但操作的是同一块内存
  • 每个端口都有自己的地址、数据和控制信号
  • 存在读写冲突问题,需要设计者处理

举个例子:端口A在地址0x100写入数据,同时端口B在地址0x100读取数据。这时候读出的数据是什么?嗯,这取决于时序。如果写操作先完成,读出的就是新数据;如果读操作先完成,读出的就是旧数据。

避坑指南:我曾经在一个图像处理项目中,因为没处理好双端口的读写冲突,导致图像出现随机噪点。排查了两天才发现问题。后来我养成了一个习惯:所有双端口设计都加上冲突检测逻辑,宁可多花几个LUT,也不能留隐患。

3. 真双端口模式

真双端口模式,也叫简单双端口模式。它跟双端口模式最大的区别是:一个端口只能写,另一个端口只能读

特点:

  • 端口A固定为写端口,端口B固定为读端口
  • 没有读写冲突,因为读写是分开的
  • 控制逻辑比双端口简单,但比单端口复杂

真双端口模式特别适合做数据缓冲。比如ADC采集数据,通过写端口存入EBR,然后CPU或DSP通过读端口取走数据。读写互不干扰,非常干净。

我个人最喜欢用真双端口模式做异步FIFO。为什么?因为读写时钟可以不同,而且不用担心冲突问题。你想想看,一个时钟域写数据,另一个时钟域读数据,中间用真双端口EBR做桥梁,多省心。

三种模式的对比与选择

为了让大家更直观地理解,我画了一张结构图:

EBR三种配置模式结构对比 单端口模式 地址总线 数据总线(双向) EBR存储阵列 16Kbit 1个端口 控制信号 双端口模式 端口A 读/写 端口B 读/写 EBR存储阵列 16Kbit 2个端口共享 控制A 控制B 真双端口模式 端口A 只写 端口B 只读 EBR存储阵列 16Kbit 读写分离 写控制 读控制

从图中可以清楚看到:单端口只有一个端口,双端口有两个可读可写的端口,真双端口则是一个只写一个只读。

那么,实际设计中怎么选?我给大家一个参考:

  • 做寄存器堆、小容量FIFO → 单端口就够了
  • 做双时钟域数据交换 → 真双端口最合适
  • 需要同时读写同一块数据 → 双端口模式,但要小心冲突

个人经验:如果拿不准选哪种模式,我建议优先考虑真双端口。虽然灵活性比双端口差一点,但胜在安全可靠。我在一个工业控制项目中,所有EBR都用了真双端口模式,三年没出过问题。

EBR的初始化与配置

EBR在上电后,里面的数据是随机的。如果你需要初始值,可以在配置时指定。Lattice的Diamond软件支持在IP核生成时设置初始值,也可以加载初始化文件。

我记得有一次调试,发现EBR读出的数据全是0xAA,吓了我一跳。后来才想起来,是我在配置时不小心把初始化文件设成了0xAA。嗯,这种低级错误,犯过一次就不会再犯了。

配置EBR时,有几个参数需要特别注意:

  • 数据位宽:1~36位,根据实际需求选择
  • 地址深度:由位宽决定,位宽越大深度越小
  • 读写模式:先读后写、先写后读、不改变
  • 输出寄存器:加一级流水线可以提升时序,但会增加一个时钟周期的延迟

重要提醒:输出寄存器这个选项,很多人会忽略。如果你的设计跑在100MHz以上,我强烈建议使能输出寄存器。否则,EBR的读延迟可能会让你的时序收敛变得非常困难。我曾经在一个200MHz的设计中,就是因为没加输出寄存器,导致setup time一直过不了。

好了,EBR的基础知识就讲到这里。三种模式各有优劣,关键是根据应用场景来选。下一节我们会深入讲解EBR的时序模型和具体配置方法,到时候我会带大家手写一个EBR的配置代码。


公众号:蓝海资料掘金营,微信deep3321