SDRAM小记
一、知识点
- 存储单元主要由列选通三极管、存储电容、刷新放大器组成。
- SRAM(异步静态随机存储器)要正确存储一位数据,需要最少6个晶体管,因此,从芯片面积上来说,单片SRAM芯片的容量不可能做到很高。
- 工作电压:SDRAM的工作电压是3.3V,DDR的工作电压是2.5V,DDR2的工作电压是1.8V
- HY57V281620 芯片,容量128Mbit,数据位宽16bit,具有4个BANK,每个BANK存储32MBit的数据。每个BANK由4096*512个存储单元组成,每个存储单元存储16bit数据。
- 控制总线:由4条控制总线来控制SDRAM:
片选信号CS_N,行选通信号RAS_N,列选通信号CAS_N,写使能信号WE_N。
13位地址总线,2位BANK地址线,2位掩码DQM - 为了保证SDRAM器件读写的稳定性,控制器的时钟和器件工作时钟均为100MHz,但是器件工作时钟理论向后偏移180°,考虑到布局布线延迟,实际不偏移。
- 为了保证存储的数据不丢失,每一行每64ms刷新一次
二、具体工作时序
- 上电初始化
module sdr_initial(
input clk,
input sdr_rst_n,
input sdr_initial_en,
output [19:0]init_bus,
output reg initial_done
);
parameter clk_period = 10;//时钟周期10ns
parameter t100us = 100000/clk_period;
parameter tRp = 30/clk_period;//延迟30ns
parameter tRfc =100/clk_period;//延迟100ns
parameter tMrd = 20/clk_period;//延迟2个时钟周期
parameter IDEL=7'b000_0001,//空闲态
s0 =7'b000_0010,//初始化状态,延迟100ns
s1 =7'b000_0100,//NOP过渡状态
s2 =7'b000_1000,//预充电
s3 =7'b001_0000,//自动刷新
s4 =7'b010_0000,//自动刷新
s5 =7'b100_0000;//加载模式寄存器
parameter NOP = 4'b0111,//空操作命令
PRE = 4'b0010,//预充电命令
AREF= 4'b0001,//自动刷新命令
MEST= 4'b0000,//加载模式寄存器命令
ACT = 4'b0011;//**命令
parameter ZEROS = 3'b000,//默认3个0
OP_CODE = 1'b0,//写突发模式设置
OP_MODE = 2'b00,//标准模式
CLs = 2,//列选通潜伏期:2
BT = 1'b0,//突发类型:顺序
BLs = 2;//突发长度:2
parameter BL=(BLs==1)?3'b000:
(BLs==2)?3'b001:
(BLs==4)?3'b010:
(BLs==8)?3'b011:3'b111;//突发长度为1 2 4 8 或者FULL PAGE(111)
parameter CL=(CLs==2)?3'b010:3'b011;//列选通潜伏期为2或者3
reg [6:0]state;
reg [15:0]cnt;
reg [1:0]init_ba;
reg [12:0]init_addr;
reg [3:0]init_cmd;
reg init_cke;
assign init_bus = {init_ba,init_addr,init_cmd,init_cke};
aaa@qq.com(posedge clk or negedge sdr_rst_n)
if(!sdr_rst_n)begin
cnt <= 16'd0;
init_cke <= 1'd0;
init_addr <= 13'd0;
init_cmd <= NOP;
initial_done <= 1'd0;
state <= IDEL;
init_ba <= 2'd0;
end
else begin
case(state)
IDEL:
begin
if(sdr_initial_en)
begin
state <= s0;
end
else begin
state <= IDEL;
cnt <= 16'd0;
init_cke <= 1'd0;
init_addr <= 13'd0;
init_cmd <= NOP;
initial_done <= 1'd0;
init_ba <= 2'd0;
end
end
s0:
begin
if(cnt >= 9999)
begin
init_cke <= 1'd1;
cnt <= 16'd0;
state <= s1;
end
else
cnt <= cnt+1'b1;
end
s1:
begin
init_addr[10] <= 1'b1;
init_cmd <= PRE;
state <= s2;
end
s2:
begin
if(cnt >=2)
begin
cnt <= 16'd0;
init_cmd <= AREF;
state <= s3;
end
else begin
cnt <= cnt+1'b1;
init_cmd <= NOP;
end
end
s3:
begin
if(cnt >=9)
begin
cnt <= 16'd0;
init_cmd <= AREF;
state <= s4;
end
else begin
cnt <= cnt+1'b1;
init_cmd <= NOP;
end
end
s4:
begin
if(cnt >=9)
begin
cnt <= 16'd0;
init_cmd <= MEST;
init_addr <= {ZEROS,OP_CODE,OP_MODE,CL,BT,BL};//模式寄存器设置
state <= s5;
end
else begin
cnt <= cnt+1'b1;
init_cmd <= NOP;
end
end
s5:
begin
if(cnt >= 1)
begin
initial_done <= 1'b1;
cnt <= 16'd0;
state <=IDEL;
end
else begin
init_cmd <= NOP;
cnt <= cnt+1'b1;
end
end
default:begin
cnt <= 16'd0;
init_cke <= 1'd1;
init_addr <= 13'd0;
init_cmd <= NOP;
initial_done <= 1'd0;
state <= IDEL;
end
endcase
end
// reg [6:0]current_state;
// reg [6:0]next_state;
// reg [15:0]cnt;
//
// aaa@qq.com(posedge clk or negedge sdr_rst_n)
// if(!sdr_rst_n)
// current_state <= IDEL;
// else
// current_state <= next_state;
//
// aaa@qq.com(*)
// begin
// case(current_state)
// IDEL:
// begin
// if(sdr_initial_en)
// next_state = s0;
// else
// next_state = IDEL;
// end
// s0:
// begin
// if(cnt>=t100us-1)
// next_state=s1;
// else
// next_state = current_state;
// end
// s1:
// begin
// next_state = s2;
// end
// s2:
// begin
// if(cnt>=tRp-1)
// next_state = s3;
// else
// next_state = current_state;
// end
// s3:
// begin
// if(cnt>=tRfc-1)
// next_state = s4;
// else
// next_state = current_state;
// end
// s4:
// begin
// if(cnt>=tRfc-1)
// next_state = s5;
// else
// next_state = current_state;
// end
// s5:
// begin
// if(cnt>=tMrd-1)
// next_state = IDEL;
// else
// next_state = current_state;
// end
// default:next_state=IDEL;
// endcase
// end
//
// aaa@qq.com(posedge clk or negedge sdr_rst_n)
// if(!sdr_rst_n)begin
// cnt <= 16'd0;
// init_cke <= 1'd0;
// init_addr <= 13'd0;
// init_cmd <= NOP;
// initial_done <= 1'd0;
// end
// else begin
// case(next_state)
// IDEL:
// begin
// cnt <= 16'd0;
// init_cke <= 1'd0;
// init_addr <= 13'd0;
// init_cmd <= NOP;
// end
// s0:
// begin
// if(cnt >= t100us-1)
// begin
// init_cke <= 1'd1;
// cnt <= 16'd0;
// end
// else
// cnt<=cnt+1'b1;
// end
// s1:
// begin
// init_addr[10] <=1'b1;
// init_cmd <= PRE;
// end
//
// s2:
// begin
// if(cnt >=tRp-1)
// begin
// cnt <= 16'd0;
// init_cmd <= AREF;
// end
// else begin
// cnt <= cnt+1'b1;
// init_cmd <= NOP;
// end
// end
// s3:
// begin
// if(cnt >=tRfc-1)
// begin
// cnt <= 16'd0;
// init_cmd <= AREF;
// end
// else begin
// cnt <= cnt+1'b1;
// init_cmd <= NOP;
// end
// end
// s4:
// begin
// if(cnt >=tRfc-1)
// begin
// cnt <= 16'd0;
// init_cmd <= MEST;
// init_addr <= 13'b0_0000_0010_0001;//模式寄存器设置
// end
// else begin
// cnt <= cnt+1'b1;
// init_cmd <= NOP;
// end
// end
// s5:
// begin
// if(cnt >= tMrd-1)
// begin
// initial_done <= 1'b1;
// cnt <= 16'd0;
// end
// else begin
// init_cmd <= NOP;
// cnt <= cnt+1'b1;
// end
// end
// default:begin
// cnt <= 16'd0;
// init_cke <= 1'd1;
// init_addr <= 13'd0;
// init_cmd <= NOP;
// initial_done <= 1'd0;
// end
// endcase
// end
endmodule
- 自动刷新
//自动刷新
module ref_fsm(
clk,
rst_n,
ref_en,
ref_bus,
ref_done
);
input clk;
input rst_n;
input ref_en;//使能自动刷新(Auto refresh)
output [19:0]ref_bus;
output reg ref_done;
reg [3:0]state;
reg [3:0]ref_cnt;//自动刷新时间间隔计数器
reg [1:0]ref_ba;
reg [12:0]ref_addr;
reg [3:0]ref_cmd;
reg ref_cke;
parameter IDEL = 4'b0001,
s0 = 4'b0010,
s1 = 4'b0100,
s2 = 4'b1000;
parameter NOP = 4'b0111,//空操作命令
PRE = 4'b0010,//预充电命令
AREF= 4'b0001,//自动刷新命令
MEST= 4'b0000,//加载模式寄存器命令
ACT = 4'b0011;//**命令
// parameter auto_refresh_pediod = 1560;
assign ref_bus = {ref_ba,ref_addr,ref_cmd,ref_cke};
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
state <= IDEL;
ref_cmd <= NOP;
ref_ba <= 2'd0;
ref_addr <= 13'd0;
ref_done <= 1'd0;
ref_cke <= 1'b1;
ref_cnt <= 4'b0;
end
else begin
case(state)
IDEL:
begin
if(ref_en) begin
state <= s0;
ref_cmd <= PRE;
ref_addr[10] <= 1'd1;
ref_done <= 1'd0;
ref_cke <= 1'b1;
end
else begin
state <= IDEL;
ref_cmd <= NOP;
ref_ba <= 2'd0;
ref_addr <= 13'd0;
ref_done <= 1'd0;
ref_cnt <= 4'b0;
ref_cke <= 1'b1;
end
end
s0:
begin
state <= s1;
ref_cmd <= NOP;
end
s1:
begin
state <= s2;
ref_cmd <= AREF;
end
s2://tRFC
begin
if(ref_cnt>=9)begin
ref_cnt <= 4'd0;
state <= IDEL;
ref_done <= 1'b1;
end
else begin
ref_cnt <= ref_cnt+1'b1;
ref_cmd <= NOP;
end
end
default:begin
state <= IDEL;
ref_cmd <= NOP;
ref_ba <= 2'd0;
ref_addr <= 13'd0;
ref_done <= 1'd0;
end
endcase
end
// aaa@qq.com(posedge clk or negedge rst_n)
// if(!rst_n)begin
// rt_flag <= 1'd0;
// cnt <= 11'd0;
// ref_almost <= 1'b0;
// end
// else if(en_cnt) begin
// if(cnt >= (auto_refresh_pediod-1))begin
// rt_flag <= 1'b1;
// cnt <= 10'd0;
// ref_almost <= 1'b0;
// end
// else if(cnt >= (auto_refresh_pediod-21))begin
// ref_almost <= 1'b1;
// rt_flag <= 1'd0;
// cnt <= cnt+1'b1;
// end
// else begin
// rt_flag <= 1'd0;
// cnt <= cnt+1'b1;
// ref_almost <= 1'b0;
// end
// end
// else begin
// rt_flag <= 1'd0;
// cnt <= 11'd0;
// ref_almost <= 1'b0;
// end
//
endmodule
-
写时序(写数据的时候没有潜伏期,在发出写明令的同时需要立即给出要写的数据)
//写状态机
module wr_fsm(rst_n,
clk,
wr_en,
row,
col,
wr_data,
ba,wr_bus, wr_done, wr_out_en, dq_out );
input rst_n;
input clk;
input wr_en;
input [12:0]row;
input [9:0]col;
input [31:0]wr_data;
input [1:0]ba;output [19:0]wr_bus;
output reg wr_done;
output reg wr_out_en;
output reg[15:0]dq_out;parameter NOP = 4’b0111,//空操作命令
PRE = 4’b0010,//预充电命令
AREF= 4’b0001,//自动刷新命令
MEST= 4’b0000,//加载模式寄存器命令
ACT = 4’b0011,//**命令
WR = 4’b0100,//写命令
RD = 4’b0101;//读命令
parameter IDEL = 4’b0001,
s0 = 4’b0010,
s1 = 4’b0100,
s2 = 4’b1000;
reg [3:0]state;
reg [2:0]cnt;
reg [1:0]wr_ba;
reg [12:0]wr_addr;
reg [3:0]wr_cmd;
reg wr_cke;
assign wr_bus = {wr_ba,wr_addr,wr_cmd,wr_cke};
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
state <= IDEL;
cnt <= 3'd0;
wr_ba <= 2'b0;
wr_addr <= 13'd0;
wr_cke <= 1'b1;
wr_cmd <= NOP;
wr_done <= 1'b0;
wr_out_en <= 1'b0;
dq_out <= 16'd0;
end
else
case(state)
IDEL:
begin
if(wr_en)begin
state <= s0; //**bank、行
wr_cmd <= ACT;
wr_ba <= ba;
wr_addr <= row;
end
else begin
state <= IDEL;
cnt <= 3'd0;
wr_ba <= 2'b0;
wr_addr <= 13'd0;
wr_cke <= 1'b1;
wr_cmd <= NOP;
wr_done <= 1'b0;
wr_out_en <= 1'b0;
dq_out <= 16'd0;
end
end
s0://延迟tRCD,再**列
begin
if(cnt >= 3'd2)begin//延迟3个周期
state <= s1;
cnt <= 3'd0;
wr_addr[10] <= 1'b1;//打开自动预充电
wr_addr[9:0] <= col;
wr_cmd <= WR;
dq_out <= wr_data[15:0];
wr_out_en <= 1'b1;
end
else begin
cnt <= cnt+1'b1;
wr_cmd <= NOP;
end
end
s1:
begin
state <= s2;
wr_cmd <= NOP;
dq_out <= wr_data[31:16];
end
s2:begin
wr_out_en <= 1'b0;
if(cnt >= 3'd4)begin //延时tWR(2 clk)以及tRP(3clk)
state <= IDEL;
wr_done <= 1'b1;
cnt <= 3'd0;
end
else begin
state <=s2;
cnt <= cnt +1'b1;
wr_done <= 1'b0;
end
end
default:begin
state <= IDEL;
cnt <= 3'd0;
wr_ba <= 2'b0;
wr_addr <= 13'd0;
wr_cke <= 1'b1;
wr_cmd <= NOP;
wr_done <= 1'b0;
wr_out_en <= 1'b0;
dq_out <= 16'd0;
end
endcase
endmodule
//
// write way 2
///
//module sdram_write(
// input sclk,
// input !s_rst_n
//
// input wr_en,
// output reg wr_req,
// output reg flag_wr_end,
//
// input ref_req,
// input wr_trig,
//
// output reg [3:0]wr_cmd,
// output reg [11:0]wr_addr,
// output reg [1:0]bank_addr
// );
//
//
// reg flag_wr;
// reg [4:0]state;
//
// reg flag_act_end;
// reg flag_pre_end;
// reg sd_row_end;
// reg [1:0]burst_cnt;
// reg [3:0]act_cnt;
// reg [1:0]burst_cnt_t;
// reg wr_data_end;//数据写完标志
// reg [3:0]break_cnt;
// reg [6:0]col_cnt;
// reg [11:0]row_addr;
// reg [8:0]col_addr;
//
//
// localparam S_IDLE = 5’b0_0001;
// localparam S_REQ = 5’b0_0010;
// localparam S_ACT = 5’b0_0100;
// localparam S_WR = 5’b0_1000;
// localparam S_PRE = 5’b1_0000;
//
//
// //flag_wr
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_wr<= 1’b0;
// else if(wr_trig && flag_wr==1’d0)
// flag_wr<= 1’b1;
// else if(wr_data_end)
// flag_wr<= 1’b0;
//
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// state <= S_IDLE;
// else case(state)
// S_IDLE:
// if(wr_trig)
// state <= S_REQ;
// else
// state <= S_IDLE;
//
// S_REQ:
// if(wr_en)
// state <= S_ACT;
// else
// state <= S_REQ;
//
//
// S_ACT:
// if(flag_act_end)
// state <= S_WR;
// else
// state <= S_ACT;
//
// S_WR:
// if(wr_data_end)
// state <= S_PRE;
// else if(ref_req && burst_cnt_t == 'd3 &&flag_wr)
// state <= S_PRE;
// else if(sd_row_end && flag_wr)
// state <= S_PRE;
//
// S_PRE;
// if(ref_req && flag_wr)
// state <= S_REQ;
// else if(sd_row_end && flag_wr)
// state <= S_ACT;
// else if(wr_data_end)
// state <= S_IDLE;
// default:
// state <= S_IDLE;
// endcase
//
//
--------------------------------------------------
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_cmd <= NOP;
// else case(state)
// S_ACT:
// if(act_cnt == 'd0);
// wr_cmd <= ACT;
// else
// wr_cmd <= NOP;
// S_WR:
// if(burst_cnt == 'd0)
// wr_cmd <= WR;
// else
// wr_cmd <= NOP;
// S_PRE:
//
// if(break_cnt == 'd0)
// wr_cmd <= PRE;
// else
// wr_cmd <= NOP;
// //
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_addr <= 'd0;
// else case(state)
// S_ACT:if(act_cnt == 'd0)
// wr_addr <= row_addr;
// S_WR:
// wr_addr <= {3’b010,col_addr};
// S_PRE:if(break_cnt =='d0)
// wr_addr[10] <= 'd1;
//
//
// // flag_act_end
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_act_end <= 1’b0;
// else if(act_cnt == 'd3)
// flag_act_end <= 1’b1;
// else
// flag_act_end <= 1’b0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// act_cnt <= 'd0;
// else if(state == S_ACT)
// act_cnt <= act_cnt +1’b1;
// else
// act_cnt <= 'd0;
//
//
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_pre_end <= 1’b0;
// else if(break_cnt == 'd3)
// flag_pre_end <= 1’b1;
// else
// flag_pre_end <=1’b0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// break_cnt <= 'd0;
// else if(state == S_PRE)
// break_cnt <= break_cnt +1’b1;
// else
// break_cnt <= 'd0;
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// burst_cnt <= 'd0;
// else if(state == S_WR)
// burst_cnt <= burst_cnt +1’b1;
// else
// burst_cnt <= 'd0;
//
//
// aaa@qq.com(posedge sclk )
// burst_cnt_t <= burst_cnt;
//
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_data_end <= 'd0;
// else if(row_addr == 'd1 && col_addr == 'd511)
// wr_data_end <= 1’b1;
// else
// wr_data_end <= 'd0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// col_cnt <= 'd0;
// else if(col_addr == 'd511)
// col_cnt <= 'd0;
// else if(burst_cnt_t == 'd3)//突发长度4
// col_cnt <= col_cnt+1’b1;
// else
// col_cnt <= col_cnt;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// row_addr <= 'd0;
// else if(sd_row_end)
// row_addr <= row_addr +1’b1;
//
//
// assign col_addr = {col_cnt,burst_cnt_t};
// assign bank_addr = 2’b00;
//
//endmodule
4. 读时序
理想读时序波形:
实际读时序波形:
//读状态机
module rd_fsm(
clk,
capture_clk,
rst_n,
rd_en,
row,
col,
sdr_dq,
ba,
rd_done,
rd_data,
rd_bus
);
input clk;
input capture_clk;
input rst_n;
input rd_en;
input [1:0]ba;
input [12:0]row;
input [9:0]col;
input[15:0]sdr_dq;
output reg rd_done;
output reg[31:0]rd_data;
output [19:0]rd_bus;
parameter NOP = 4'b0111,//空操作命令
PRE = 4'b0010,//预充电命令
AREF= 4'b0001,//自动刷新命令
MEST= 4'b0000,//加载模式寄存器命令
ACT = 4'b0011,//**命令
WR = 4'b0100,//写命令
RD = 4'b0101;//读命令
parameter IDEL = 6'b000001,
s0 = 6'b000010,
s1 = 6'b000100,
s2 = 6'b001000,
s3 = 6'b010000,
s4 = 6'b100000;
parameter cl=2,//列选通潜伏期 2
sl=2;//同步数据潜伏期 :前仿真设置为1,后仿真改为2
reg [1:0]rd_ba;
reg [12:0]rd_addr;
reg [3:0]rd_cmd;
reg rd_cke;
reg [5:0]state;
reg [2:0]cnt;
reg load_h;
reg load_l;
assign rd_bus = {rd_ba,rd_addr,rd_cmd,rd_cke};
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n) begin
state <= IDEL;
cnt <= 3'b0;
rd_ba <= 2'b0;
rd_addr <= 13'b0;
rd_cmd <= NOP;
rd_cke <= 1'b1;
rd_done <= 1'b0;
load_l <= 1'b0;
load_h <= 1'b0;
end
else begin
case(state)
IDEL:
begin
if(rd_en)begin
state <= s0;
rd_cmd <= ACT;
rd_addr <= row;
rd_ba <= ba;
end
else begin
state <= IDEL;
cnt <= 3'b0;
rd_ba <= 2'b0;
rd_addr <= 13'b0;
rd_cmd <= NOP;
rd_cke <= 1'b1;
rd_done <= 1'b0;
load_l <= 1'b0;
load_h <= 1'b0;
end
end
s0:
begin
state <= s1;
rd_cmd <= NOP;
end
s1:
begin
state <= s2;
rd_cmd <= RD;
rd_addr <= {3'b001,col};
rd_ba <= ba;
end
s2:
begin
if(cnt >= cl+sl-1)begin
cnt <= 3'd0;
state <= s3;
load_l <= 1'b1;
end
else begin
cnt <= cnt+1'b1;
state <=s2;
rd_cmd <= NOP;
end
end
s3:
begin
load_h <= 1'b1;
load_l <= 1'b0;
state <= s4;
end
s4:
begin
load_h <= 1'b0;
rd_done <= 1'b1;
state <= IDEL;
end
default:begin
state <= IDEL;
cnt <= 3'b0;
rd_ba <= 2'b0;
rd_addr <= 13'b0;
rd_cmd <= NOP;
rd_cke <= 1'b1;
rd_done <= 1'b0;
load_l <= 1'b0;
load_h <= 1'b0;
end
endcase
end
reg [15:0] dq_capture;
reg [15:0] dq_sys;
aaa@qq.com(posedge capture_clk)
dq_capture <= sdr_dq;
aaa@qq.com(posedge clk)
dq_sys <= dq_capture;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
rd_data <= 32'd0;
else if(load_l)
rd_data[15:0] <= dq_sys;
else if(load_h)
rd_data[31:16] <= dq_sys;
endmodule
三、主状态机
//主状态机
module main_fsm(clk,
rst_n,
local_addr,
col,
row,
ba,
local_ready,//状态显示:1=不忙,可以进行读写;0=忙
init_en,
initial_done,
ref_en,
ref_done,
rt_en,
rt_flag,
// ref_almost,
local_wr,
wr_en,
wr_done,
w_data,
local_wdata,
local_read,
rd_en,
rd_done,
r_data,
local_rdata,
local_rdatavalid,
s_mux
);
input clk;
input rst_n;
input [24:0]local_addr;
output reg local_ready;
output reg[12:0]row;
output reg[9:0]col;
output reg[1:0]ba;
output reg init_en;
input initial_done;
input ref_done;
output reg ref_en;
output reg rt_en;//自动刷新使能信号
input rt_flag;
// input ref_almost;//距离下次刷新的时间不足以完成一次读或写的时刻
input local_wr;
output reg wr_en;
input wr_done;
input [31:0]local_wdata;
output reg[31:0]w_data;
input local_read;
output reg rd_en;//读使能
input rd_done; //读完成
input [31:0]r_data;
output reg [31:0]local_rdata;//读出数据
output reg local_rdatavalid;//读数据有效信号
output reg[2:0]s_mux;
parameter s0 = 6'b000001,
s1 = 6'b000010,
s2 = 6'b000100,
s3 = 6'b001000,
s4 = 6'b010000,
s5 = 6'b100000;
parameter INIT = 3'd1,
REF = 3'd2,
WR = 3'd3,
RD = 3'd4;
reg [5:0]state;
reg ref_trigger;
//寄存自动使能信号 ,如果系统处于仲裁状态(s3),则直接进入自动刷新模式;如果系统处于读写阶段,则寄存下
//该自动刷新命令会寄存在ref_trigger寄存器内,直到读写结束重新回到仲裁状态,产生ref_en信号使能刷新,并将
//ref_trigger 置低。自动刷新时间为15us,远远高于15.6us的最低要求,富裕的时间足以完成一次读写突发。
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
ref_trigger <= 1'b0;
else if(rt_flag)
ref_trigger <= 1'b1;
else if(ref_en)
ref_trigger <= 1'b0;
else
ref_trigger <= ref_trigger;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n) begin
state <= s0;
ref_en <= 1'b0;
rt_en <= 1'b0;
init_en <= 1'b0;
s_mux <= INIT;
wr_en <= 1'b0;
rd_en <= 1'b0;
w_data <= 32'd0;
col <= 10'd0;
row <= 13'd0;
ba <= 2'b0;
local_ready <= 1'b0;
local_rdata <= 32'd0;
local_rdatavalid <= 1'b0;
end
else begin
case(state)
s0:
begin
init_en <= 1'b1;
state <= s1;
end
s1://
begin
if(initial_done) begin
state <= s2;
ref_en <= 1'b1;
rt_en <= 1'b1;
s_mux <= REF;
end
else begin
init_en <= 1'b0;
state <= s1;
end
end
s2://自动刷新完成后进入等待命令状态
begin
ref_en <= 1'b0;
if(ref_done)begin
state <= s3;
s_mux <= REF;
local_ready <= 1'b1;
end
else
state <= s2;
s_mux <= REF;
local_ready <= 1'b0;
end
s3://仲裁状态:等待读写、自动刷新命令
begin
if(ref_trigger)begin
state <= s2;
ref_en <= 1'b1;
local_ready <= 1'b0;
end
else if(local_wr)begin
state <= s4;
wr_en <= 1'b1;
s_mux <= WR;
local_ready <= 1'b0;
w_data <= local_wdata;
row <= local_addr[22:10];
col <= local_addr[9:0];
ba <= local_addr[24:23];
end
else if(local_read) begin
state <= s5;
rd_en <= 1'b1;
s_mux <= RD;
local_ready <= 1'b0;
row <= local_addr[22:10];
col <= local_addr[9:0];
ba <= local_addr[24:23];
local_rdatavalid <= 1'b0;
end
else begin
state <= s3;
s_mux <= REF;
local_rdatavalid <= 1'b0;
ref_en <= 1'b0;
wr_en <= 1'b0;
local_ready <= 1'b1;
end
end
s4:
begin
if(wr_done)begin
state <= s3;
local_ready <= 1'b1;
s_mux <= REF;
end
else begin
state <= s4;
wr_en <= 1'b0;
end
end
s5:
begin
if(rd_done)begin
state <= s3;
s_mux <= REF;
local_ready <= 1'b1;
local_rdatavalid <= 1'b1;
local_rdata <= r_data;
end
else begin
state <= s5;
rd_en <= 1'b0;
end
end
default:begin
state <= s0;
ref_en <= 1'b0;
rt_en <= 1'b0;
init_en <= 1'b0;
s_mux <= REF;
wr_en <= 1'b0;
rd_en <= 1'b0;
w_data <= 32'd0;
col <= 10'd0;
row <= 13'd0;
ba <= 2'b0;
local_ready <= 1'b0;
local_rdata <= 32'd0;
local_rdatavalid <= 1'b0;
end
endcase
end
endmodule
四、顶层模块
//=======================================\
// 前仿真sdr_clk设置为180度,同步潜伏期设置为1
// 后仿真sdr_clk设置为0度,同步潜伏期设置为2
// capture_clk均设置为180度
//=======================================/
module sdram_ctrl_top(
clk50M,
sdr_clk,
rst_n,
sdr_ba,
sdr_addr,
sdr_dqm,
sdr_dq,
sdr_CS_N,
sdr_RAS_N,
sdr_CAS_N,
sdr_WE_N,
sdr_CKE,
local_wdata,//待写入数据
local_rdata,//读出的数据
local_ready,//可以进行读写操作信号
local_addr,//待写入数据的行列BANK地址
local_read,
local_wr,
local_rdatavalid,
clk
);
input clk50M;
output sdr_clk;
input rst_n;
output [1:0]sdr_ba;
output [12:0]sdr_addr;
output [1:0]sdr_dqm;
inout [15:0]sdr_dq;
output sdr_CS_N;
output sdr_RAS_N;
output sdr_CAS_N;
output sdr_WE_N;
output sdr_CKE;
input [31:0]local_wdata;//待写入数据
output [31:0]local_rdata;//读出的数据
output local_ready;//可以进行读写操作信号
input [24:0]local_addr;//待写入数据的行列BANK地址
input local_read;
input local_wr;
output local_rdatavalid;
output clk;
wire sys_rst_n;
wire locked;
//------------------------------------------------------------------
wire capture_clk;
wire [9:0]col;
wire [12:0]row;
wire [1:0]ba;
wire init_en;
wire initial_done;
wire ref_en;
wire ref_done;
wire rt_en;
wire rt_flag;//刷新计数器记满标志
// wire ref_almost;
wire wr_en;
wire wr_done;
wire [31:0]w_data;
wire rd_en;
wire rd_done;
wire [31:0]r_data;
wire [2:0]s_mux;
wire [19:0]init_bus;
wire [19:0]wr_bus;
wire wr_out_en;
wire [15:0]dq_out;
wire [19:0]ref_bus;
wire [19:0]rd_bus;
wire [19:0]s_bus;
assign sys_rst_n = rst_n && locked;//待PLL输出稳定后停止复位
assign sdr_dq = wr_out_en?dq_out:{16{1'bz}};//设置三态门
assign sdr_dqm = 2'b0;
assign {sdr_ba,sdr_addr,sdr_CS_N,sdr_RAS_N,sdr_CAS_N,sdr_WE_N,sdr_CKE} = s_bus;
// assign {sdr_ba,sdr_addr,sdr_CS_N,sdr_RAS_N,sdr_CAS_N,sdr_WE_N,sdr_CKE} = (s_mux == INIT)?init_bus:
// (s_mux == REF)?ref_bus :
// (s_mux == WR)?wr_bus:rd_bus;
pll_sdram PLL_SDRAM(
.areset(1'b0),
.inclk0(clk50M),
.c0(clk),
.c1(sdr_clk),
.c2(capture_clk),
.locked(locked)
);
main_fsm MAIN_FSM(
.clk(clk),
.rst_n(sys_rst_n),
.local_addr(local_addr),
.col(col),
.row(row),
.ba(ba),
.local_ready(local_ready),
.init_en(init_en),
.initial_done(initial_done),
.ref_en(ref_en),
.ref_done(ref_done),
.rt_en(rt_en),
.rt_flag(rt_flag),
// .ref_almost(ref_almost),
.local_wr(local_wr),
.wr_en(wr_en),
.wr_done(wr_done),
.w_data(w_data),
.local_wdata(local_wdata),
.local_read(local_read),
.rd_en(rd_en),
.rd_done(rd_done),
.r_data(r_data),
.local_rdata(local_rdata),
.local_rdatavalid(local_rdatavalid),
.s_mux(s_mux)
);
sdr_initial SDR_INITIAL(
.clk(clk),
.sdr_rst_n(sys_rst_n),
.sdr_initial_en(init_en),
.init_bus(init_bus),
.initial_done(initial_done)
);
ref_fsm REF_FSM(
.clk(clk),
.rst_n(sys_rst_n),
.ref_en(ref_en),
.ref_bus(ref_bus),
.ref_done(ref_done)
);
ref_rt_cnt REF_RT_CNT(
.rst_n(sys_rst_n),
.clk(clk),
.en_cnt(rt_en),
.rt_flag(rt_flag)
// .ref_almost(ref_almost)
);
wr_fsm WR_FSM(
.rst_n(sys_rst_n),
.clk(clk),
.wr_en(wr_en),
.row(row),
.col(col),
.wr_data(w_data),
.ba(ba),
.wr_bus(wr_bus),
.wr_done(wr_done),
.wr_out_en(wr_out_en),
.dq_out(dq_out)
);
rd_fsm RD_FSM(
.clk(clk),
.capture_clk(capture_clk),
.rst_n(sys_rst_n),
.rd_en(rd_en),
.row(row),
.col(col),
.sdr_dq(sdr_dq),
.ba(ba),
.rd_done(rd_done),
.rd_data(r_data),
.rd_bus(rd_bus)
);
mux_bus MUX_BUS(
.s_mux(s_mux),
.wr_bus(wr_bus),
.rd_bus(rd_bus),
.ref_bus(ref_bus),
.init_bus(init_bus),
.s_bus(s_bus)
);
endmodule
上一篇: Linux下强制卸载安装的php方法实例
下一篇: Spring mvc环境搭建