我们的SDRAM控制器具有以下特点:
控制器最罕见的功能可能是最后一个。SDRAM是单端口存储器,但FPGA可以通过访问双端口存储器(如blockrams)获益匪浅,因此我们认为这是一个很好的功能。
这是我们的SDRAM控制器的简单视图。
写入代理使用左侧的前三个信号(“写入请求”,后面跟着“写入地址”和“写入数据”)。然后低于阅读代理的三个信号。在右侧,控制器驱动SDRAM信号。为了说明问题,下面是使用我们的SDRAM控制器的典型FPGA系统的视图。
现在我们的控制器使SDRAM显示为双端口存储器。但是SDRAM实际上是一个单端口内存,所以我们的控制器必须发挥作用。如果我们的控制器同时获得两个请求,它不得不停止一个代理,或记录他们的请求并在稍后执行。我们的控制者选择第一种策略。所以我们添加了“授权”信号:代理可以在任何时候声明一个请求,但控制器有权授予请求或不授权。如果请求被拒绝,请继续询问,最终将被授予。
SDRAM的最后一个复杂性来自于读取请求返回的数据延迟(称为SDRAM数据表中的CAS延迟)。控制器也可能会增加几个时钟的延迟。因此,即使控制器可能立即授予读取请求,匹配数据只能在固定数量的时钟后才可用。为了方便起见,我们添加了一个“数据有效”信号,当数据真正可用时,该信号被断言。
我们的SDRAM控制器的心脏是一台状态机。控制器等待请求(读取或写入),打开匹配的存储体/行,发出读取或写入命令(只要活动代理在活动行中请求它们),最后关闭该行。
有了这个计划,一次只有一家银行活跃起来。先进的SDRAM控制器可以同时激活多个存储体,但我们决定保持简单。
现在,打开和关闭行需要时间。例如,我们的SDRAM数据表提供了这些数字:
状态机现在看起来有点复杂。
最后,可能需要调整NOP周期的数量。例如,在100MHz和tRP = 21ns的情况下,预充电后实际需要两个NOP周期(在下一次激活之前给我们30ns)。
SDRAM有一些可编程设置(如CAS延迟),因此需要在上电后初始化“MODE”寄存器。“LOAD MODE”命令用于此目的。SDRAM初始化可以添加到控制器中,或者在控制器运行之前的一个单独步骤中添加。
我们的控制器的核心如下所示。
为了获得尽可能好的IO时序,所有的SDRAM控制信号都被注册,以便FPGA中不存在组合逻辑信号。
alway @(posedge clk)//状态机 case(state) 2'h0:begin ,if(RdReq | WrReq)开始 //是否有读或写请求? SDRAM_CMD <= SDRAM_CMD_ACTIVE; //如果是的话,打开 SDRAM_BA <= Addr [19]; //这家银行 SDRAM_A <= Addr [18:8]; //这一行 SDRAM_DQM <= 2'b11; state<= 2'h1; end others begin SDRAM_CMD <= SDRAM_CMD_NOP; //保持空闲状态 SDRAM_BA <= 0; SDRAM_A <= 0; SDRAM_DQM <= 2'b11; 状态<= 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]}; //列 SDRAM_A [10] <= 1'b0; //没有自动预充电 SDRAM_DQM <= 2'b00; 状态<=(ReadSelected?RdReq:WrReq)&SameRowAndBank?2'h1:2'h2; end 2'h2:begin SDRAM_CMD <= SDRAM_CMD_PRECHARGE; //完成后关闭该行 SDRAM_BA <= 0; SDRAM_A <= 11'b100_0000_0000; //所有银行预充电 SDRAM_DQM <= 2'b11; 状态<= 2'h0; end endcase
完整的演示代码在这里可用。它是功能性的,但由于这是教育性的,我们通过删除非基本功能尽可能简化它。检查代码中的注释以了解限制和要求。
SDRAM通常用于视频卡,因为需要大量内存来存储图形。它的工作方式如下:计算机的CPU将图形数据发送到视频卡。该卡使用SDRAM来存储数据,并且卡中的控制器定期读取存储器以将数据发送到显示器。数据(在SDRAM中)在此过程中自动刷新。
我们使用Xylo-EM板创建了这样的基本系统,作为我们验证过程的一部分。SDRAM控制器是双端口的,这使设计变得简单(PC / FX2是写代理,视频控制器是阅读代理)。