一、SDRAM基本操作
SDRAM主要有4个基本行为操作,分别是上电初始化,读操作,写操作和刷新。
1.1 上电初始化时序
SDRAM的上电顺序如上所示:
1.首先等待至少100us,CKE设置为高,命令为NOP。
2.在100us结束后,即可发出一个全部Bank的预充电命令(Precharge all),其中地址总线的A10=1,表示所有Bank预充电,A10=0,对指定bank充电。
3.等待至少tRP,此时的命令为NOP。
4.发出一个自动刷新命令(Auto Refresh)。
5.等待至少tRFC,此时的命令为NOP。
6.再发出一个自动刷新命令(Auto Refresh)。
7.再等待至少tRFC,此时的命令为NOP。
8.使用LMR命令设置模式寄存器,就是对地址总线A[12:0]进行操作,
其中A[2:0]表示突发长度 000:突发长度为1 001:突发长度为2 010:突发长度为4 011:突发长度为8 其余位保留
A[3] 表示突发类型 0表示顺序 1表示逆序
A[6:4] 表示CAS latency 就是潜伏期是多少个时钟周期 010:2 011:3 其余保留
A[9] 表示写突发模式 1:单一写 0:突发写
9:等待至少tMRD,此时的命令为NOP
10:上电初始化时序完成
module sdram_init(
//system signals
input sclk,
input rst_n,
//others
output reg [3:0] cmd_reg, //{sdr_cs_n,sdr_ras_n,sdr_cas_n,sdr_we_n}
output reg [12:0] sdram_addr,
output reg flag_init_end
);
localparam S0=3'b000,
S1=3'b001,
S2=3'b010,
S3=3'b011,
S4=3'b100,
S5=3'b101,
S6=3'b110;
localparam NOP=4'b0111,
PRE=4'b0010,
REF=4'b0001,
LMR=4'b0000,
OP=13'b0_0000_0011_0010; // 突发写,突发长度4,CAS latency为3,突发写,顺序
localparam tRP =2, //2
tRFC=7, //7
tMRD=2, //2
T100us=10000; //20000
reg [14:0] cnt;
reg [2:0] state;
always@(posedge sclk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
cmd_reg <=NOP;
sdram_addr <=13'b1_1111_1111_1111;
state <=S0;
flag_init_end <='d0;
cnt <='d0;
end
else
case(state)
S0:if(cnt<T100us-1) //等待至少100us
begin
cmd_reg <=NOP;
sdram_addr <=13'b1_1111_1111_1111;
state <=S0;
flag_init_end <='d0;
cnt <=cnt+1'b1;
end
else
begin
state <=S1;
cnt <='d0;
end
S1: begin //全部Bank的预充电命令
cmd_reg <=PRE;
state <=S2;
sdram_addr[10]<=1'b1;
end
S2:if(cnt<tRP-1) // 等待至少tRP时间
begin
cmd_reg <=NOP;
state <=S2;
cnt <=cnt+1'b1;
end
else //发出一个自动刷新命令
begin
state <=S3;
cnt <='d0;
cmd_reg <=REF;
end
S3:if(cnt<tRFC-1) //等待至少tRFC
begin
cmd_reg <=NOP;
state <=S3;
cnt <=cnt+1'b1;
end
else //再发出一个自动刷新命令
begin
state <=S4;
cnt <='d0;
cmd_reg <=REF;
end
S4:if(cnt<tRFC-1) //等待至少tRFC
begin
cmd_reg <=NOP;
state <=S4;
cnt <=cnt+1'b1;
end
else //使用LMR命令设置模式寄存器
begin
state <=S5;
cnt <='d0;
cmd_reg <=LMR;
sdram_addr <=OP;
end
S5:if(cnt<tMRD-1) //等待至少tMRD
begin
cmd_reg <=NOP;
sdram_addr <=13'b1_1111_1111_1111;
state <=S5;
cnt <=cnt+1'b1;
end
else
begin
state <=S6; //上电初始化时序完成
cnt <='d0;
flag_init_end <='d1;
end
S6: state <=S6;
default: state <=S0;
endcase
end
endmodule
1.2 读操作时序
1.首先执行行激活命令,命令为ACT,地址总线A[12:0]设置为要激活的行地址row_addr
2.等待tRCD时间,此时命令为NOP
3.tRCD时间之后,执行读命令READ,地址总线A[12:0]设置为要读的列地址{3b000,col_addr},注意此时采用的SDRAM,行地址为13位,列地址为10位
4:潜伏周期后,数据出现在Dq总线上
module Rd_fsm(
//输入
rd_en,
rd_row,
rd_col,
rd_ba,
soft_rst_n,
clk,
capture_clk,
dq_in,
//输出
rd_bus,
rdata,
rd_done,
load_l,
load_h
);
input clk,capture_clk;
input soft_rst_n;
input rd_en;
input [12:0] rd_row;
input [9:0] rd_col;
input [1:0] rd_ba;
input [15:0] dq_in;
output reg [19:0] rd_bus;// rd_bus[19:7]=sdr_a,rd_bus[6:5]=sdr_ba,rd_bus[4:1]={sdr_cs_n,sdr_ras_n,sdr_cas_n,sdr_we_n},rd_bus[0]=sdr_cke
output reg [31:0] rdata;
output reg rd_done;
reg [15:0] dq_cap,dq_sym;
reg [2:0] state;
reg [2:0] cnt;
output reg load_l;
output reg load_h;
localparam S0=3'b000;
localparam S1=3'b001;
localparam S2=3'b010;
localparam S3=3'b011;
localparam S4=3'b100;
always@(posedge clk or negedge soft_rst_n)
begin
if(!soft_rst_n)
begin
rd_done <= 1'b0;
rd_bus[19:7] <= 13'd0;
rd_bus[6:5] <= 2'd0;
rd_bus[4:1] <= `NOP;
rd_bus[0] <= 1'b1;
cnt <= 3'd0;
load_l <= 1'b0;
load_h <= 1'b0;
state <= S0;
end
else
case(state)
S0:if(!rd_en)
begin
state <= S0;
end
else
begin
rd_bus[4:1] <= `ACT; // 激活行命令
rd_bus[19:7] <= rd_row; //行地址A[12:0]
rd_bus[6:5] <= rd_ba; //BANK 地址
rd_done <= 1'b0;
state <= S1;
end
S1:if(cnt<`tRCD-1) //等待tRCD时间
begin
state <= S1;
rd_bus[4:1] <= `NOP;
cnt <= cnt+1'b1;
end
else
begin
rd_bus[4:1] <= `RD; //读命令READ
rd_bus[16:7] <= rd_col; //列地址A[9:0]
rd_bus[6:5] <= rd_ba; //BANK 地址
rd_bus[17] <= 1'b1; //自动预充电
cnt <= 3'd0;
state <= S2;
end
S2:if(cnt<`CL+`SL-1) // 潜伏周期后
begin
state <= S2;
rd_bus[4:1] <= `NOP;
cnt <= cnt+1'b1;
end
else
begin
load_l <= 1'b1;
state <= S3;
cnt <= 3'd0;
end
S3: begin
load_l <= 1'b0;
load_h <= 1'b1;
state <= S4;
end
S4: begin
load_h <= 1'b0;
rd_done <= 1'b1;
state <= S0;
end
default: state <= S0;
endcase
end
always@(posedge capture_clk)
begin
dq_cap <= dq_in;
end
always@(posedge clk)
begin
dq_sym <= dq_cap;
end
always@(posedge clk)
begin
if(!soft_rst_n)
rdata <= 32'd0;
else if(load_l)
rdata[15:0] <= dq_sym;
else if(load_h)
rdata[31:16] <= dq_sym;
end
endmodule
1.3 写操作时序
1.首先执行行激活命令,命令为ACT,地址总线A[12:0]设置为要激活的行地址row_addr,和读操作一样
2.等待tRCD时间,此时命令为NOP
3.tRCD时间之后,执行读命令Write,地址总线A[12:0]设置为要读的列地址{3b000,col_addr},注意此时采用的SDRAM,行地址为13位,列地址为10位。sdram写操作没有潜伏周期概念,直接将数据写入数据总线上。
module Wr_fsm(
//输入
wr_en,
wr_row,
wr_col,
wr_ba,
wdata,
soft_rst_n,
clk,
//输出
wr_bus, //wr_bus
wr_done,
dq_out,
dq_en
);
input soft_rst_n;
input clk;
input wr_en;
input [12:0] wr_row;
input [9:0] wr_col;
input [1:0] wr_ba;
input [31:0] wdata;
output reg [19:0] wr_bus; // wr_bus[19:7]=sdr_a,wr_bus[6:5]=sdr_ba,wr_bus[4:1]={sdr_cs_n,sdr_ras_n,sdr_cas_n,sdr_we_n},wr_bus[0]=sdr_cke
output reg wr_done;
output reg [15:0] dq_out;
output reg dq_en;
reg [1:0] state;
reg [1:0] cnt;
localparam S0=2'b00;
localparam S1=2'b01;
localparam S2=2'b10;
localparam S3=2'b11;
always@(posedge clk)
begin
if(!soft_rst_n)
begin
wr_bus[19:7] <= 13'd0;
wr_bus[6:5] <= 2'd0;
wr_bus[4:1] <= `NOP;
wr_bus[0] <= 1'b1;
dq_en <= 1'b0;
dq_out <= 16'd0;
state <= S0;
wr_done <= 1'b0;
cnt <= 2'd0;
end
else
case(state)
S0:if(!wr_en)
begin
state <= S0;
end
else
begin
wr_bus[4:1] <= `ACT; // 激活行命令
wr_bus[19:7] <= wr_row; //行地址A[12:0]
wr_bus[6:5] <= wr_ba; //BANK 地址
wr_done <= 1'b0;
state <= S1;
end
S1:if(cnt<`tRCD-1) //等待tRCD时间
begin
state <= S1;
cnt <= cnt+1'b1;
wr_bus[4:1] <= `NOP;
end
else
begin
state <= S2;
wr_bus[4:1] <= `WR; //写命令READ
wr_bus[6:5] <= wr_ba; //BANK 地址
wr_bus[16:7] <= wr_col; //列地址A[9:0]
wr_bus[17] <= 1'b1; //自动预充电
dq_en <= 1'b1;
dq_out <= wdata[15:0];
cnt <= 2'd0;
end
S2: begin
wr_bus[4:1] <= `NOP;
dq_out <= wdata[31:16];
state <= S3;
end
S3: begin
dq_en <= 1'b0;
wr_done <= 1'b1;
state <= S0;
end
endcase
end
endmodule
1.4 自刷新操作时序
SDRAM的自刷新操作是必须的,由已知SDRAM是动态随机存取存储器,一段时间不进行刷新,所存储的数据就会出错。
自刷新和预充电都是对行进行充电,不同的区别是,预充电是对之前执行完的行进行操作,而自刷新是从第一行自动递增到最后一行进行充电,我们这里是约每7.8us刷新一行,每经过7.8us,执行自刷新操作。
1.首先执行预充电,为之前读写操作的行充电,命令为PRE
2.等待tRP时间
3.执行自刷新操作,命令为REF
4.等待tRFC时间后,自刷新操作结束
module Ref_fsm(
//输入
ref_en,
soft_rst_n,
clk,
//输出
ref_done,
ref_bus
);
input ref_en;
output reg ref_done;
input soft_rst_n;
input clk;
output reg[19:0] ref_bus;// ref_bus[19:7]=sdr_a,ref_bus[6:5]=sdr_ba,ref_bus[4:1]={sdr_cs_n,sdr_ras_n,sdr_cas_n,sdr_we_n},ref_bus[0]=sdr_cke
reg [1:0] state;
reg [3:0] cnt;
localparam S0=3'b000;
localparam S1=3'b001;
localparam S2=3'b010;
localparam S3=3'b011;
always@(posedge clk)
begin
if(!soft_rst_n)
begin
ref_bus[19:7] <= 13'd0; //边界
ref_bus[6:5] <= 2'd0;
ref_bus[4:1] <= `NOP;
ref_bus[0] <= 1'd1;
cnt <= 4'd0;
ref_done <= 1'd0;
state <= S0;
end
else
begin
case(state)
S0:if(ref_en==1'b1)
begin
state <= S1;
ref_bus[4:1] <= `PRE;
ref_bus[17] <= 1'b1;
cnt <= 4'd0;
end
else
begin
state <= S0;
end
S1:if(cnt<`tRP-1)
begin
cnt <=cnt+1'b1;
state <=S1;
ref_bus[4:1] <= `NOP;
end
else
begin
cnt <= 4'd0;
state <=S2;
ref_bus[4:1] <= `REF;
end
S2:if(cnt<`tRFC-1)
begin
cnt <= cnt+1'b1;
ref_bus[4:1] <= `NOP ;
state <= S2;
end
else
begin
cnt <= 4'd0;
state <= S3;
ref_done <= 1'd1;
end
S3: begin
state <= S0;
ref_done <= 1'd0;
end
endcase
end
end
endmodule