FPGA之SDRAM控制器设计(一)
程序员文章站
2024-02-25 14:51:09
...
MT48LC128M4A2 – 32 Meg x 4 x 4 banks是512M SRAM,总体概述如下图
分别从上电初始化,刷新,写,读四个部分进行设计,此外还包含主控状态机,一个顶层。
1:上电初始化
整体架构:从控制器到要控制的芯片可以分成20位的bus总线,时钟线sdr_clk,数据总线DQ以及DQM。上电时候主要是对bus总线的高4位也就是sdr_cmd进行配置。也就是要做的第一步,比较简单就是对sdr_cmd进行几次操作。其中注意在逻辑设计时候输出的sdr_clk时钟要和控制器的时钟相差180°,实际上不是这样的后仿的时候要根据有效窗口调动。软复位soft_rst_n,是由pll分频器产生的稳定信号打两拍得到的。
下图是sdr_cmd命令功能对应的数值,对于上电如果感觉对英文文档读的比较枯燥那先别care,主要把下面这张图的命令看懂。。例如一个命令INHIBIT(禁止)CS#,RAS#,CAS#,WE#设置为1xxx,后面三位don’t care。
下面引脚从CLK到DQ是通过FPGA进行控制时要用到的,各个功能在右侧有描述。
下图是初始上电的时序图,上电过程做到Tp+2即可。在上电要等100us待供电和时钟稳定后进行操作,用到的时钟是100MHz,需要一个计数器记到100us后发出下一个命令NOP,再到预充电PRECHARGE,等待tRP时间后再AUTO REFRESH,后面的看下图。tRP,Trfc,Tmrd,这些参数在手册上都有,在下面设计文件中头文件有涉及到。就按照这个时序进行初始化的设计状态转移图就可有可无。
头文件:
//开关参数
`define tRP 2
`define tRFC 7
`define tMRD 2
`define T100us 10000
`define tWCD 2
`define tRCD 2
`define SL 2
`define CL 2
//命令cmd={cs_n,ras_n,cas_n,we_n}
`define INH 4'b1000
`define NOP 4'b0111
`define ACT 4'b0011
`define RD 4'b0101
`define WR 4'b0100
`define BT 4'b0110 //burst terminal
`define PRE 4'b0010
`define REF 4'b0001
`define LMR 4'b0000
//模式寄存器设置
`define OP 13'b0_0000_0010_0001
//多路选择器开关
`define MUX_INIT 2'b00
`define MUX_REF 2'b01
`define MUX_WR 2'b10
`define MUX_RD 2'B11
初始化设计:
`include "head.v"
module init_fsm(
clk,init_en,local_wdata,local_rdata,local_write,
local_read,local_ready,local_addr,local_rddatavalid,
sdr_a,sdr_ba,init_cmd,sdr_cke,init_done,soft_rst_n,init_bus
);
input clk;
input init_en;
input soft_rst_n;
input [31:0] local_wdata;
output [31:0] local_rdata;
input local_read,local_write,local_ready;
input [24:0] local_addr;
input local_rddatavalid;
output reg [12:0] sdr_a;
output reg [1:0] sdr_ba;
output reg [3:0] init_cmd;
output reg sdr_cke;
output reg init_done;
output [19:0] init_bus;
reg [14:0] cnt;
reg [2:0] state;
localparam s0 = 3'b000;
localparam s1 = 3'b001;
localparam s2 = 3'b010;
localparam s3 = 3'b011;
localparam s4 = 3'b100;
localparam s5 = 3'b101;
localparam idle = 3'b110;
assign init_bus = {init_cmd,sdr_a,sdr_ba,sdr_cke};
always @(posedge clk)
begin
if(soft_rst_n == 1'b0)
begin
sdr_a <= 'd0;
sdr_ba <= 'd0;
init_cmd <= `INH;//inhabit禁止命令INH= 4'b1000 8
sdr_cke <= 1'b0;
cnt <= 'd0;
init_done <= 1'b0;
state <= idle;//idle=6
end
else
case(state)
idle : if(init_en == 1'b0)
state <= idle;
else
begin
init_done <= 1'b0;
state <= s0;
end
s0 : if(cnt < `T100us - 1)
begin
cnt <= cnt +1'b1;
state <= s0;
end
else
begin
cnt <= 'd0;
sdr_cke <= 1'b1;
init_cmd <= `NOP;//空操作`NOP =4'b0111 7
state <= s1;
end
s1 : begin
init_cmd <= `PRE;//预充电`PRE= 4'b0010 2
sdr_a[10] <= 1'b1;
state <= s2;
end
s2 : if(cnt < `tRP - 1)//tRP=2
begin
cnt <= cnt +1'b1;
init_cmd <= `NOP;//NOP=0111 7
state <= s2;
end
else
begin
cnt <= 'd0;
init_cmd <= `REF;// REF= 4'b0001 1
state <= s3;
end
s3 : if(cnt < `tRFC - 1'b1 )//tRFC=7
begin
cnt <= cnt + 1'b1;
init_cmd <= `NOP;
state <= s3;
end
else
begin
cnt <= 'd0;
init_cmd <= `REF;// 1
state <= s4;
end
s4 : if(cnt < `tRFC - 1)//7
begin
cnt <= cnt + 1'b1;
init_cmd <= `NOP;
end
else
begin
cnt <= 'd0;
init_cmd <= `LMR;//load mode register
sdr_a <= `OP;
state <= s5;
end
s5 : if(cnt < `tMRD - 1)//tMRD= 2
begin
cnt <= cnt + 1'b1;
init_cmd <= `NOP;
state <= s5;
end
else
begin
cnt <= 'd0;
init_done <= 1'b1;
state <= idle;
end
endcase
end
endmodule
初始化的顶层文件只需要将上面的module例化一下在加上下面一段代码即可
always @(posedge clk or posedge global_reset)//输出稳定的软复位信号
if(global_reset == 1'b1)
begin
r0 <= 1'b0;
r1 <= 1'b0;
r2 <= 1'b0;
soft_rst_n <= 1'b0;
end
else
begin : delay_2clk
r0 <= locked;
r1 <= r0;
r2 <= r1;
soft_rst_n <= r2;
end