新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > FPGA:SDRAM控制器

FPGA:SDRAM控制器

作者:时间:2024-01-11来源:EEPW编译收藏

尽管现代 包含内部存储器,但可用存储器量始终比专用存储芯片低几个数量级。 因此,许多设计人员将某种类型的存储器附加到他们的中也就不足为奇了。 特别是,SDRAM因其高速和低成本而成为非常受欢迎的存储器。 不幸的是,它们不像静态存储器那样容易控制,因此经常使用

本文引用地址:http://www.eepw.com.cn/article/202401/454693.htm

对于我们的控制器,我们的目标是可能是最简单的SDRAM:美光MT48LC1M16A1 16Mb传统SDRAM。 我们的测试系统包括 Xylo-E、Xylo-EM 和 Xylo-LM(具有 16Mb 至 256Mb SDRAM 的 Xilinx 或 Altera FPGA)。

我们将主题分为三个部分。 首先介绍用于 FPGA 的存储器。 然后,我们将展示如何制作一个16Mb 。 最后,介绍了先进的DDR SDRAM。

SDRAM 1 - 从静态到动态

静态记忆

假设我们想将一个 16Mb 的存储器连接到 FPGA。

16Mb 表示内存可容纳 16 万位(准确地说是 16,777,216 位)。 现在,这些位很少单独寻址,而是通常以 8 或 16 个数据包的形式进行寻址(我们称之为单词)。 因此,如果我们的 16Mb 内存被组织成 1M 字的 16 位,我们需要一个 20 位地址总线和一个 16 位数据总线,以及一些写使能和读使能信号。

真正的存储器也会有一个CS(芯片选择),如果存储器是同步的,则有一个时钟(为了清楚起见,图片中省略了这些时钟)。

现在,如果这个存储器是一个模块(在FPGA内部),它看起来会有所不同(假设存在如此大的16Mb模块......典型的块状结构要小得多)。

正如你所看到的,它仍然是一个内存块,但有两个地址总线。 这是因为现代 FPGA 中的模块是双端口的......这意味着两个代理可以同时访问内存。 通常,一个代理写入内存,而另一个代理读取。 因此,内存为每个代理提供独立的地址和数据总线。 上图显示了顶部的第一个(写入)代理和底部的第二个(读取)代理。 更高级的 blockram 允许每个代理同时读取和写入,但上面显示的架构是最常用的。 此外,blockram 通常是同步使用的,因此每个代理都必须提供一个时钟(图片中未显示)。

到目前为止显示的记忆是静态的,这意味着它们只需通过施加力量来保存其内容。 静态存储器可以看作是一长串单词(您只需提供一个地址并访问匹配的数据......无并发症)。 权衡是,每比特的成本远高于...

动态记忆

我们将使用SDRAM,它是一个动态内存(SDRAM中的“D”)。 在动态记忆中,记忆不被视为单词的长线性数组,而是组织为单词矩阵(行/列)。

上图显示行 12 位,列 8 位,和以前一样总共 20 个地址位......容易。

有一个复杂因素:为了提高性能,内存被分成相等的块,称为“bank”。 这是因为某些动态内存操作速度很慢,因此拥有 bank 允许在等待另一个 bank 的同时使用一个 bank。

因此,如果我们的 16Mb SDRAM 有 2 个存储区,则每个存储区拥有 8Mb。

当访问SDRAM时,FPGA必须选择bank(1位)、行(现在只有11位)和列(8位),总共20位。 但这是一个两步过程:首先是行+库,然后是列:

  • FPGA 选择一个 bank 并激活其中一行。然后它等待几个时钟(等待行准备就绪)。

  • 现在,该行处于活动状态,FPGA只需提供列地址即可访问(读取和/或写入)行中所需的任何数据。

  • 一旦FPGA完成了对该行的处理,它必须在打开新行之前关闭该行。

为了获得最佳性能,用户(=FPGA)希望避免过多地打开和关闭行,而是在关闭行之前尽可能多地连续做工作,并在不同的bank中交错操作,这样就不会浪费时钟周期。 大多数 SDRAM 实际上有 4 或 8 个 bank,每个 bank 都是独立的,因此可以有自己的活动行。

SDRAM 引脚

如果我们看一下SDRAM的引脚,就会发现有一些新来者。

地址和数据总线仍然存在,但地址总线只有 11 位(它用于提供要打开的行,然后是列地址)。 BA 引脚指定 bank(由于我们的示例中只有 2 个 bank,因此我们只需要一个引脚)。 WE、CAS 和 RAS 一起用作命令引脚,因此我们可以向 SDRAM 发送 8 个不同的命令(命令包括“打开行”、“读取”、“写入”和“关闭行”)。 还使用了其他一些引脚(但未在图片中显示),例如时钟、芯片选择、字节使能......

请注意,在动态内存术语中,“打开行”称为激活,“关闭行”称为预充电。

刷新

SDRAM 是动态存储器:它们的内容需要定期刷新,因为每个存储器位值(0 或 1)都保存在一个微小的电容器中,其电荷会随着时间的推移而衰减。 但衰减率足够低,只要定期读取和重写(“刷新”),制造商保证不会丢失任何数据。

有两种刷新机制可用。

  • FPGA向SDRAM发送“auto-refresh”命令。
    只需确保定期完成(SDRAM数据表会告诉您最小刷新频率)。

  • FPGA“足够频繁”地访问每一行。
    打开一行会导致SDRAM中的“检测放大器”获得该行所有电容电荷的副本。 然后,当行关闭时,先前检测到的值被复制回电容器中,从而刷新过程中的数据。

为什么选择内存控制器?

动态记忆比静态记忆更复杂......我们有行、列、库和刷新周期需要处理。 但SDRAM因其高速和低每比特成本而引人注目。

因此,我们需要的是一种访问 SDRAM 的方法,但要易于使用静态内存。 这就是创建内存控制器的原因。 它们充当转换层:一方面,它们为用户提供易于使用的存储器接口,然后执行繁琐的工作来驱动真正的SDRAM信号。

SDRAM 2 - 一个简单的控制器

我们的具有以下特点:

  • 易于使用:使SDRAM看起来像一个静态存储器(或尽可能接近)。

  • 快速:如果提供连续地址,则在突发模式下使用 SDRAM。

  • 简单:没有调度,一次只有一家银行处于活动状态。

  • 功能强大:SDRAM 表现为简单的双端口存储器(即具有一个写入代理和一个读取代理)。

控制器最不常见的功能可能是最后一个功能。 SDRAM 是单端口存储器,但 FPGA 从访问双端口存储器(如块式存储器)中受益匪浅,因此我们认为这是一个不错的功能。

控制器信号

以下是SDRAM控制器的简单视图。

左边的前三个信号由写入代理使用(“写入请求”,然后是“写入地址”和“写入数据”)。 然后是阅读代理的三个信号。 在右侧,控制器驱动SDRAM信号。 为了正确看待问题,这里是使用我们的SDRAM控制器的典型FPGA系统的视图。

现在,我们的控制器使SDRAM显示为双端口存储器。 但是SDRAM实际上是一个单端口存储器,所以我们的控制器必须玩一个技巧。 如果我们的控制器同时收到两个请求,它要么必须停止一个代理,要么记录他们的请求并在以后执行它们。 我们的控制器选择了第一种策略。 因此,我们添加了“授予”信号:代理可以随时断言请求,但控制器有权授予或不授予请求。 如果请求被拒绝,请继续询问,它最终会被批准。

SDRAM 的最后一个复杂因素是,从读取请求返回的数据会延迟(在 SDRAM 数据表中称为 CAS 延迟)。 控制器还可能增加一些延迟时钟。 因此,即使控制器可能会立即授予读取请求,匹配的数据也只能在固定数量的时钟后可用。 为方便起见,我们添加了一个“数据有效”信号,该信号在数据真正可用时被断言。

状态机

我们的SDRAM控制器的核心是状态机。 控制器等待请求(读取或写入),打开匹配的库/行,发出读取或写入命令(只要活动代理在活动行中请求这些命令),最后关闭该行。

使用此方案,一次只有一家银行处于活动状态。 先进的SDRAM控制器将允许多个bank同时处于活动状态,但我们决定保持简单。

现在,打开和关闭行需要时间。 例如,我们的SDRAM数据表提供了以下数字:

  • tRCD = 20ns(激活读/写)

  • tRP = 21ns(预充电至激活状态)

因此,如果我们以 100MHz 的频率运行 SDRAM,时钟周期为 10ns,我们需要在状态机中添加一些空时钟周期(称为 NOP)。
此外,如果读取代理无法保证定期打开每一行,则需要刷新周期。

状态机现在看起来有点复杂。

最后,可能需要调整 NOP 循环次数。 例如,在 100MHz 且 tRP = 21ns 时,我们实际上需要在预充电后进行两个 NOP 周期(在下一次激活之前给我们 30ns)。

初始化

SDRAM具有一些可编程设置(如CAS延迟),因此需要在上电后初始化“MODE”寄存器。 为此,使用“load mode”命令。 SDRAM 初始化可以添加到控制器中,也可以在控制器运行之前添加到单独的步骤中。

代码

我们控制器的核心如下所示。
为了获得最佳的IO时序,所有SDRAM控制信号都经过注册,这样就不会有组合逻辑信号进入FPGA之外。

always @(posedge clk)  // state machinecase(state)
    2'h0: begin
    	if(RdReq | WrReq) begin  // is there a read or write request?
    		SDRAM_CMD <= SDRAM_CMD_ACTIVE;  // if so, open
    		SDRAM_BA <= Addr[19];  // this bank
    		SDRAM_A <= Addr[18:8];  // this row
    		SDRAM_DQM <= 2'b11;
    		state <= 2'h1;    	end
    	else
    	begin
    		SDRAM_CMD <= SDRAM_CMD_NOP;  // otherwise stay idle
    		SDRAM_BA <= 0;
    		SDRAM_A <= 0;
    		SDRAM_DQM <= 2'b11;
    		state <= 2'h0;    	end
    end
    2'h1: begin
    	SDRAM_CMD <= ReadSelected ? SDRAM_CMD_READ : SDRAM_CMD_WRITE;
    	SDRAM_BA <= AddrR[19];
    	SDRAM_A[9:0] <= {2'b00, AddrR[7:0]};  // column
    	SDRAM_A[10] <= 1'b0;  // no auto-precharge
    	SDRAM_DQM <= 2'b00;
    	state <= MoreRequestsInSameBankAndRow ? 2'h1 : 2'h2;    end
    2'h2: begin
    	SDRAM_CMD <= SDRAM_CMD_PRECHARGE;  // close the row when we're done with it
    	SDRAM_BA <= 0;
    	SDRAM_A <= 11'b100_0000_0000;  // all banks precharge
    	SDRAM_DQM <= 2'b11;
    	state <= 2'h0;    endendcase

完整的演示代码可在此处获得。 它是功能性的,但由于这是教育性的,我们通过删除非必要功能来使其尽可能简单。 检查代码中的注释,了解限制和要求。

使用示例

SDRAM 通常用于视频卡,因为存储图形需要大量内存。 它的工作原理是这样的:计算机的 CPU 将图形数据发送到视频卡。 该卡使用SDRAM来存储数据,卡中的控制器定期读取存储器以将数据发送到显示器。 在此过程中,数据(在 SDRAM 中)会自动刷新。

作为验证过程的一部分,我们使用 Xylo-EM 板创建了这样的基本系统。 SDRAM 控制器是双端口的,这使得设计变得简单明了(PC/FX2 是写入代理,视频控制器是读取代理)。

SDRAM 3 - DDR

随着内存制造商试图找到提高 SDRAM 速度的方法,DDR SDRAM 应运而生。 DDR 代表“双倍数据速率”。

DDR SDRAM 的大脑类似于常规(单数据速率)SDRAM(我们仍然有 bank 和行要打开)。 DDR SDRAM 的支路要快得多,具有更快的时钟速度、更长的突发周期,并且每个时钟周期发送的每条数据线有两个位(因此得名 DDR)。 这导致数据线上的时序非常紧张,并添加了一些功能来帮助可靠地传输数据。



关键词: FPGA SDRAM控制器

评论


相关推荐

技术专区

关闭