明德扬科教业务:
FPGA培训班
FPGA项目承接
虽然现代FPGA包含内部存储器,但可用内存的数量总是低于专用存储器芯片所能达到的水平的数量级。因此,许多FPGA设计者将某种类型的内存附加到他们的FPGA上也就不足为奇了。特别是SDRAM以其高速度和低成本的特点而成为非常受欢迎的存储器。不幸的是,它们不像静态存储器那样容易控制,所以经常使用SDRAM控制器。
对于我们的控制器,我们的目标可能是最简单的SDRAM:微米。MT48LC1M16A116 MB遗留SDRAM。我们的测试系统是Xylo-E,Xylo-EM和Xylo-LM(Xilinx或AlteraFPGAs,16 MB到256 MB SDRAM).
我们将主题分为三个部分。首先介绍了FPGA中使用的存储器。然后介绍了如何制作16 MB的SDRAM控制器。最后介绍了先进的DDR SDRAMS。
-
从静态到动态
-
一个简单的控制器
-
DDR-速度竞赛
静态记忆
让我们假设我们希望将一个16 MB的内存连接到一个FPGA上。
16 MB意味着内存容纳1600万位(确切地说是16,777,216位)。现在这些位很少是单独寻址的,但是通常是在8或16的数据包中(我们称之为单词)。因此,如果我们的16 MB内存被组织为1M字的16位,我们需要一个20位的地址总线和一个16位的数据总线,再加上一些允许写和读的信号。
真正的内存也会有一个CS(芯片选择),如果内存是同步的,则会有一个时钟(为了清晰起见,从图片中省略这些时钟)。
如果这个内存是一个块(在FPGA内),它看起来就不一样了(假设有这么大的16 MB块).典型的区块要小得多)。
正如您所看到的,它仍然是一个内存块,但是有两个地址总线。那是因为现代FPGA中的块是双口的.这意味着两个代理可以同时访问内存。通常,一个代理写入内存,而另一个代理读取。因此,存储器对每个代理都有独立的地址和数据总线。上面的图片显示了顶部的第一个(写)代理和底部的第二个(读取)。更高级的阻止器允许每个代理同时读写,但上面所示的体系结构是最常用的。另外,块通常是同步使用的,因此每个代理必须提供一个时钟(没有显示在图片上)。
到目前为止,显示的记忆是静态的,这意味着它们仅仅是通过对它们施加能量来保存它们的内容。此外,静态内存可以被看作是一个长长的线性单词数组(您只需提供一个地址并可以访问匹配的数据.无并发症)。交换条件是每比特的成本比.
动态记忆
我们将使用SDRAM,它是动态内存(SDRAM中的“D”)。而在动态内存中,内存并不被看作是一长串单词,而是被组织成一个矩阵(行/列)。
上面的图片显示了行的12位和列的8位,总共有20个地址位。轻松点。
有一个复杂的问题:为了提高性能,内存被分割成相等的块,称为“银行”。这是因为一些动态内存操作很慢,所以银行允许在等待另一个银行的同时使用一个银行。
因此,如果我们的16 MB SDRAM有2家银行,那么每家银行都持有8MB。
当SDRAM被访问时,FPGA必须选择银行(1位)、行(现在只有11位)和列(8位),总共20位。但是这是两个步骤:首先是行+银行,然后是列:
-
FPGA选择一个银行并激活它的一行。然后,它等待几个时钟(为排准备)。
-
现在行是活动的,FPGA只需提供列地址就可以访问(读和/或写)行中它想要的任何数据。
-
一旦FPGA完成了对行的处理,就必须在打开新行之前关闭该行。
为了获得最好的性能,用户(=FPGA)希望避免过多地打开和关闭行,而是在关闭行之前尽可能多地进行一行工作,并在不同的银行中交叉操作,这样就不会浪费时钟周期。大多数SDRAM实际上有4或8家银行,而且每家银行都是独立的,因此可以有自己的行活动。
SDRAM引脚
如果我们看一下SDRAM的引脚,就会发现有几个新来的。
地址总线和数据总线仍然存在,但是地址总线只有11位(它用来提供要打开的行,然后是列地址)。BA引脚指定银行(因为在我们的示例中只有2家银行,我们只需要一个引脚)。we、CAS和RAS一起用作命令引脚,因此我们可以向SDRAM发送8个不同的命令(命令包括“Open row”、“read”、“write”和“Close row”)。其他一些引脚被使用(但没有显示在图片上),如时钟,芯片选择,字节启用.
请注意,在动态内存术语中,“打开行”称为“激活”,“关闭行”称为“预充电”。
刷新
SDRAM是动态存储器:它们的内容需要定期刷新,因为每个内存位值(0或1)都保存在一个微小的电容器中,其电荷随时间而衰减。但衰变率很低,只要定期读写(“刷新”),制造商就保证不会丢失任何数据。
有两种刷新机制可用。
-
FPGA向SDRAM发送“自动刷新”命令.
只需确保它是定期完成的(SDRAM数据表告诉您最低刷新频率)。
-
FPGA访问每一行“足够频繁”。
打开一行将导致SDRAM中的“感测放大器”获得该行的所有电容器电荷的副本。然后,当行被关闭时,先前感测的值被复制回电容器中,刷新进程中的数据。
为什么是内存控制器?
动态记忆比静态记忆更复杂.我们有行、列、银行和刷新周期需要处理。但是SDRAM由于其高速和低成本的每比特而引人注目。
因此,我们需要的是一种访问SDRAM的方法,但是使用静态内存很容易。这就是创建内存控制器的原因。它们充当转换层:一方面,它们为用户提供了一个易于使用的内存接口,然后进行肮脏的工作来驱动真实的SDRAM信号。
我们的SDRAM控制器具有以下特性:
-
易于使用:使SDRAM看起来像静态内存(或尽可能接近)。
-
快速:如果提供连续地址,SDRAM将在突发模式下使用。
-
简单:没有调度,一次只有一个银行在活动。
-
功能强大:SDRAM显示为一个简单的双口存储器(即有一个写入代理和一个读取代理)。
控制器最不常见的特性可能是最后一个。SDRAM是一种单端口存储器,但是FPGA从访问双口存储器(比如块)中获益很大,所以我们觉得这是一个很好的特性。
控制器信号
这里是我们的SDRAM控制器的一个简单的视图。
左边的前三个信号由书写代理使用(“写请求”,后面是“写地址”和“写数据”)。然后到下面三个信号为阅读代理。在右边,控制器驱动SDRAM信号。为了正确看待问题,这里有一个使用SDRAM控制器的典型FPGA系统的视图。
现在,我们的控制器使SDRAM显示为双口存储器.但是SDRAM实际上是一个单端口内存,所以我们的控制器不得不玩一个把戏。如果我们的控制器同时收到两个请求,它要么必须暂停一个代理,要么记录它们的请求,然后执行它们。我们的控制器选择了第一种策略。因此,我们添加了“授予”信号:代理可以在任何时候断言请求,但控制器有权授予请求或不授予请求。如果一个请求被拒绝,继续询问,它最终会被批准。
SDRAM的最后一个复杂之处在于从读取请求返回的数据被延迟(称为CA延迟在SDRAM数据表中)。控制器还可能会增加一些延迟时钟。因此,即使控制器可以立即批准读取请求,匹配的数据也只能在固定数量的时钟之后使用。为了方便起见,我们添加了一个“数据有效”信号,该信号是在数据真正可用时断言的。
状态机
我们的SDRAM控制器的心脏是一个状态机。控制器等待请求(读或写),打开匹配的银行/行,发出读或写命令(只要活动代理在活动行中请求它们),最后关闭行。
根据这一计划,一次只有一家银行在活动。先进的SDRAM控制器将允许多个银行同时活动,但我们决定保持简单。
现在,打开和结束行需要时间。例如,SDRAM数据表提供了以下数字:
-
tRCD=20 ns(主动读写)
-
Trp=21 ns(预充电至活动)
因此,如果我们在100 MHz上运行SDRAM,时钟周期是10 ns,我们需要在状态机中添加一些空时钟周期(称为NOP)。
此外,如果读取代理无法保证定期打开每一行,则需要刷新周期。
状态机现在看起来有点复杂。
最后,NOP周期的数目可能需要调整。例如,在100 MHz和Trp=21 ns,我们实际上需要两个NOP周期后,预充电(给我们30 ns前下一个激活)。
初始化
SDRAM有一些可编程的设置(比如CAS延迟),所以有一个“模式”寄存器需要在启动后初始化。为此,将使用“加载模式”命令。SDRAM初始化可以在控制器中添加,或者在控制器运行之前在单独的步骤中添加。
密码
控制器的心脏如下所示。
为了获得最佳的IO时序,所有SDRAM控制信号都被注册,这样就没有组合逻辑信号进入FPGA之外。
always @(posedge clk) // state machine case(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 <= (ReadSelected ? RdReq : WrReq) & SameRowAndBank ? 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; end endcase
完整的演示代码是可用的。这里。它是功能,但因为这是教育,我们保持它尽可能简单,通过删除非必要的功能。检查代码中的注释以了解限制和要求。
使用实例
SDRAM经常用于显卡,因为存储图形需要大量内存。它的工作原理是这样的:计算机的CPU将图形数据发送到视频卡。卡使用SDRAM存储数据,卡中的控制器定期读取存储器以将数据发送到显示器。数据(在SDRAM中)在进程中被自动刷新。
作为验证过程的一部分,我们使用Xylo-EM板创建了这样的基本系统。SDRAM控制器是双口的,这使得设计变得简单明了(PC/FX2是写入代理,视频控制器是读取代理)。
随着内存制造商试图寻找提高SDRAMS速度的方法,DDR SDRAM被引入。DDR代表“双数据速率”。
DDRSDRAM的大脑类似于常规的(单一数据速率)SDRAM(我们仍然有银行和行需要打开)。DDRSDRAM的运行速度要快得多,时钟速度更快,突发周期更长,每个时钟周期发送的数据行有两个位(因此DDR名称)。这导致数据线上的时间非常紧张,并增加了一些功能,以帮助可靠地传输数据。